diff --git a/docs/public/images/graph_clean.png b/docs/public/images/graph_clean.png
index 18d14a12..a89a51cf 100644
Binary files a/docs/public/images/graph_clean.png and b/docs/public/images/graph_clean.png differ
diff --git a/pyproject.toml b/pyproject.toml
index 8a4cc246..edd7689f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "cityseer"
-version = '4.16.25'
+version = '4.16.26'
 description = "Computational tools for network-based pedestrian-scale urban analysis"
 readme = "README.md"
 requires-python = ">=3.10, <3.14"
diff --git a/pysrc/cityseer/tools/graphs.py b/pysrc/cityseer/tools/graphs.py
index 652c6719..c8bfcb9d 100644
--- a/pysrc/cityseer/tools/graphs.py
+++ b/pysrc/cityseer/tools/graphs.py
@@ -163,7 +163,8 @@ def nx_remove_filler_nodes(nx_multigraph: MultiGraph) -> MultiGraph:
             agg_geom: ListCoordsType = []
             edge_info = util.EdgeInfo()
             while True:
-                edge_data: EdgeData = nx_multigraph[trailing_nd][next_link_nd][0]
+                # cast to list and take first in cases where key at index 0 may have been deleted
+                edge_data: EdgeData = list(nx_multigraph[trailing_nd][next_link_nd].values())[0]
                 edge_info.gather_edge_info(edge_data)
                 # aggregate the geom
                 try:
@@ -263,21 +264,7 @@ def nx_remove_dangling_nodes(
             f"specified by the remove_disconnected parameter, which is currently set to: {remove_disconnected}. "
             "Decrease the remove_disconnected parameter or set to zero to retain graph components."
         )
-    # finds connected components - this behaviour changed with networkx v2.4
-    connected_components: list[list[NodeKey]] = list(nx.algorithms.components.connected_components(nx_multigraph))
-    # keep connected components greater than remove_disconnected param
-    large_components = [component for component in connected_components if len(component) >= remove_disconnected]
-    large_subgraphs = [nx.MultiGraph(nx_multigraph.subgraph(component)) for component in large_components]
-    if not large_subgraphs:
-        logger.warning(
-            f"An empty graph will be returned because all graph components had fewer than {remove_disconnected} nodes. "
-            "Decrease the remove_disconnected parameter or set to zero to retain graph components."
-        )
-    # make a copy of the graph using the largest component
-    g_multi_copy = nx.MultiGraph()
-    for subgraph in large_subgraphs:
-        g_multi_copy.add_nodes_from(subgraph.nodes(data=True))
-        g_multi_copy.add_edges_from(subgraph.edges(data=True))
+    g_multi_copy = nx_multigraph.copy()
 
     # remove danglers
     if despine > 0:
@@ -285,9 +272,10 @@ def nx_remove_dangling_nodes(
         nd_key: NodeKey
         for nd_key in tqdm(g_multi_copy.nodes(data=False), disable=config.QUIET_MODE):
             if nx.degree(g_multi_copy, nd_key) == 1:
-                # only a single neighbour, so index-in directly and update at key = 0
+                # only a single neighbour, so index-in directly and update at first neighbour
                 nb_nd_key: NodeKey = list(nx.neighbors(g_multi_copy, nd_key))[0]
-                edge_data = g_multi_copy[nd_key][nb_nd_key][0]
+                # cast to list and take first in cases where key at index 0 may have been deleted
+                edge_data = list(g_multi_copy[nd_key][nb_nd_key].values())[0]
                 if (
                     edge_data["geom"].length <= despine
                     or ("is_tunnel" in edge_data and edge_data["is_tunnel"] is True)
@@ -296,8 +284,26 @@ def nx_remove_dangling_nodes(
                     remove_nodes.append(nd_key)
         g_multi_copy.remove_nodes_from(remove_nodes)
 
+    # clean up nodes at ex-dangler intersections
     g_multi_copy = nx_remove_filler_nodes(g_multi_copy)
 
+    # finds connected components - this behaviour changed with networkx v2.4
+    # do this after to prevent creation of new isolated components after dropping tunnels
+    connected_components: list[list[NodeKey]] = list(nx.algorithms.components.connected_components(g_multi_copy))
+    # keep connected components greater than remove_disconnected param
+    large_components = [component for component in connected_components if len(component) >= remove_disconnected]
+    large_subgraphs = [nx.MultiGraph(g_multi_copy.subgraph(component)) for component in large_components]
+    if not large_subgraphs:
+        logger.warning(
+            f"An empty graph will be returned because all graph components had fewer than {remove_disconnected} nodes. "
+            "Decrease the remove_disconnected parameter or set to zero to retain graph components."
+        )
+    # make a copy of the graph using the largest component
+    g_multi_copy = nx.MultiGraph()
+    for subgraph in large_subgraphs:
+        g_multi_copy.add_nodes_from(subgraph.nodes(data=True))
+        g_multi_copy.add_edges_from(subgraph.edges(data=True))
+
     return g_multi_copy
 
 
@@ -654,7 +660,19 @@ def _squash_adjacent(
     # find highest priority OSM highway tag
     if prioritise_by_hwy_tag:
         prioritise_tag = None
-        for osm_hwy_tag in ["motorway", "trunk", "primary", "secondary", "tertiary", "residential"]:
+        for osm_hwy_tag in [
+            "motorway",
+            "motorway_link",
+            "trunk",
+            "trunk_link",
+            "primary",
+            "primary_link",
+            "secondary",
+            "secondary_link",
+            "tertiary",
+            "tertiary_link",
+            "residential",
+        ]:
             for nd_key in node_group:
                 nb_hwy_tags = _gather_nb_tags(nx_multigraph, nd_key, "highways")
                 if osm_hwy_tag in nb_hwy_tags:
diff --git a/test.gpkg b/test.gpkg
new file mode 100644
index 00000000..556a9532
Binary files /dev/null and b/test.gpkg differ