How to Fix Disconnected Edges in Utility Topology
Disconnected edges in a utility network topology are a graph-integrity failure that quietly corrupts every downstream result: trace paths terminate early, isolation boundaries land in the wrong place, and outage-impact counts under-report affected customers. At the scale of a real distribution system — hundreds of thousands of conductors, mains, or conduits edited daily by field crews — hunting and re-snapping these breaks by hand in an editing session is neither auditable nor repeatable, and a single missed gap can invalidate a safety-critical isolation plan. The reliable fix is a schema-aware, scripted procedure that diagnoses the cause before it touches geometry, repairs within a defined tolerance, and proves the repair with a re-run trace.
Environment Prerequisites
Confirm every item below before running any repair. A mismatch in any one of them is the most common reason a “successful” snap still leaves the edge logically disconnected.
- ArcGIS Pro 3.1 or later with the Utility Network user type extension licensed and the network owner credentials available.
- Enterprise or file geodatabase hosting the Utility Network, with the topology enabled and no active schema locks (close all editing sessions and reconcile/post outstanding versions).
- Python 3.9+ with
arcpyfrom the ArcGIS Procondaenvironment (clone the defaultarcgispro-py3env so dependencies stay isolated). - Known XY tolerance and spatial reference for the network — typically
0.001–0.01 mdepending on coordinate system precision and capture method. This must be confirmed against the source data, not assumed. - Read/write access to the connectivity rule table so association rules for the affected asset types can be inspected and, if missing, added.
- A validated baseline: run a clean topology validation pass first so you are repairing genuine breaks, not stale dirty areas.
Schema-Aware Validation Protocol (Before You Snap)
Disconnected edges arise from three failure modes, and the order in which you check them matters — fixing geometry before fixing the schema simply re-creates the break on the next validate. Work the list top-down; the most common root cause is first.
- Spatial tolerance misalignment (most common). When CAD imports, field GPS captures, or legacy migrations introduce micro-gaps smaller than your capture precision but larger than the network XY tolerance, the topology engine refuses to connect coincident-looking features. Compare the network’s
XY Toleranceproperty against the source dataset’s resolution before adjusting anything. This is the same precision discipline covered in CRS alignment and geodetic transformations — a datum or projection drift upstream surfaces here as systematic snapping failure. - Missing or invalid connectivity rules. Each asset type needs an explicit connectivity association rule for its lifecycle state. If a feature class has no valid policy, the topology ignores spatial intersections entirely — the geometry is perfect and the edge is still orphaned. Audit the rule table the same way you would when configuring connectivity rules for pipe and cable, confirming the
FromAssetType/ToAssetTypepair is permitted. - Lifecycle state mismatch. An edge marked
InServicecannot logically connect to a junction in aRetiredstate without an approved transition rule, even when the two are spatially coincident. CheckLifecycleStatuson both endpoints before assuming a geometry problem.
Materialize the evidence rather than eyeballing it: run a targeted validation against the Edge Connectivity and Junction-Edge rules, then export the resulting error features to a geodatabase table so the repair script reads from a deterministic input. This diagnostic-first pass is exactly the discipline formalized in the parent automated error handling and flagging workflow.
Minimal Reproducible Implementation
The routine below extracts edge endpoints, identifies those with no junction within tolerance, snaps the flagged endpoints, rebuilds the connectivity associations, and returns a structured result for logging. It handles geoprocessing failures explicitly and cleans up its in-memory scratch datasets so it is safe to schedule. Adjust the configuration block to your network — the tolerance value in particular must match the network spatial-reference precision you confirmed above.
import arcpy
import logging
# Structured logging for an auditable repair trail.
logging.basicConfig(
filename="un_edge_remediation.log",
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
)
# --- Configuration (confirm every value against your network) ---
gdb = r"C:\UtilityNetwork\Network.gdb"
un_name = "UN_Electric"
edge_fc = f"{un_name}\\DistributionLine"
junction_fc = f"{un_name}\\Junction"
tolerance = 0.005 # Meters — MUST match the UN spatial-reference precision.
arcpy.env.workspace = gdb
arcpy.env.overwriteOutput = True
def repair_disconnected_edges():
"""Detect, snap, and re-associate disconnected edge endpoints.
Returns a dict with the count of endpoints flagged and repaired so the
caller (CI job, work-order hook) can act on a structured result.
"""
result = {"flagged": 0, "snapped": False}
endpoints_fc = "memory\\edge_endpoints_temp"
spatial_join = "memory\\spatial_validation_temp"
try:
# 1. Extract both endpoints of every edge as point features.
arcpy.management.FeatureVerticesToPoints(edge_fc, endpoints_fc, "BOTH_ENDS")
# 2. Spatial join: which endpoints have NO junction within tolerance?
arcpy.analysis.SpatialJoin(
endpoints_fc, junction_fc, spatial_join,
join_operation="JOIN_ONE_TO_ONE",
match_option="WITHIN_A_DISTANCE",
search_radius=f"{tolerance} Meters",
)
# 3. Endpoints with Join_Count = 0 are the disconnected ones.
arcpy.management.MakeFeatureLayer(spatial_join, "unconnected_lyr", "Join_Count = 0")
flagged = int(arcpy.management.GetCount("unconnected_lyr")[0])
result["flagged"] = flagged
if flagged == 0:
logging.info("No disconnected edges detected. Topology integrity verified.")
return result
logging.warning("Identified %d disconnected edge endpoints.", flagged)
arcpy.management.CopyFeatures("unconnected_lyr", "memory\\unconnected_flagged")
# 4. Snap the flagged endpoints to the nearest junction within tolerance.
snap_env = [[junction_fc, "VERTEX", f"{tolerance} Meters"]]
arcpy.edit.Snap(edge_fc, snap_env)
# 5. Rebuild connectivity associations for the corrected edges.
arcpy.un.Associate(
in_utility_network=gdb,
association_type="CONNECTIVITY",
from_feature=edge_fc,
to_feature=junction_fc,
)
result["snapped"] = True
logging.info("Applied spatial snap and rebuilt connectivity associations.")
return result
except arcpy.ExecuteError:
logging.error("Geoprocessing failed: %s", arcpy.GetMessages(2))
raise
except Exception as exc: # noqa: BLE001 — log then re-raise for the scheduler.
logging.error("Unexpected error: %s", exc)
raise
finally:
for temp_ds in (endpoints_fc, spatial_join):
if arcpy.Exists(temp_ds):
arcpy.management.Delete(temp_ds)
if __name__ == "__main__":
outcome = repair_disconnected_edges()
logging.info("Repair outcome: %s", outcome)
Geometric alignment is not topological validity. After the script runs, re-prove the result: call arcpy.un.ValidateTopology(gdb) across the affected tier, then run a Find Connected trace from each repaired junction. A trace that returns zero features or terminates early means the geometry snapped but the logical connection did not form — return to the connectivity-rule and LifecycleStatus checks above. These connected traces are the same primitives used by the upstream and downstream tracing algorithms that consume your repaired graph, so passing them here is the real acceptance test.
Production Deployment Pattern
To make this repeatable rather than a one-off rescue, embed the routine in the same governed pipeline that protects the rest of the network:
- Run against an isolated version. Execute the repair in a dedicated named version, then reconcile and post after the re-validation trace passes. Never snap directly against
DEFAULTwhile crews are editing. - Gate it in CI/CD. Wire the script into your validation pipeline so a pull request that touches edge feature classes runs the detect-and-trace check; fail the build if any endpoint stays at
Join_Count = 0or a connected trace returns zero features. This mirrors the gating approach in automating connectivity-rule validation in CI pipelines. - Add retry/backoff for locks. Enterprise geodatabases will occasionally reject
ValidateTopologyunder a transient schema lock. Wrap the validate-and-repair call in a bounded exponential-backoff retry rather than failing the whole job. - Emit a structured delta report. Persist the
flagged/snappedresult plus the modified featureGlobalIDs to your CMMS or a REST endpoint so each automated repair produces a defensible audit record. - Schedule pre-commit checks. Run the tolerance and
IsConnected = Falsechecks before data promotion, blocking edits whoseConnectivityRuleIDis null — catching the break at ingest, in line with the data ingestion pipelines for utility assets, is far cheaper than repairing it downstream.
For tool syntax and parameter validation, anchor your implementation to the official ArcGIS Pro Utility Network toolbox reference.
Conclusion
This procedure converts disconnected-edge repair from a manual editing chore into a deterministic, auditable pipeline: diagnose the schema and tolerance cause first, snap and re-associate within a confirmed tolerance, then prove integrity with a re-run connected trace. Automating it eliminates orphaned graph segments before they distort isolation planning, outage modeling, or regulatory reporting, and the structured delta report it emits satisfies asset-lifecycle audit requirements. Run it on an isolated version, gate it in CI/CD, and the same break stops recurring. The natural next step is to extend this gating across all topology errors via batch topology processing with Python.