{"record":{"id":"356e8554c1476d4a","repo":"a-b-street/abstreet","slug":"and-don-t-share-a-common-endpoint","errorCode":null,"errorMessage":"{} and {} don't share a common endpoint","messagePattern":"(.+?) and (.+?) don't share a common endpoint","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"blockfinding/src/lib.rs","lineNumber":644,"sourceCode":"            }\n            let keep_lane_orientation = if pair[0].road == pair[1].road {\n                // We're doubling back at a dead-end. Always follow the orientation of the lane.\n                true\n            } else {\n                match lane1.common_endpoint(lane2) {\n                    CommonEndpoint::One(i) => i == lane1.dst_i,\n                    CommonEndpoint::Both => {\n                        // Two different roads link the same two intersections. I don't think we\n                        // can decide the order of points other than seeing which endpoint is\n                        // closest to our last point.\n                        if let Some(last) = pts.last() {\n                            last.dist_to(pl.first_pt()) < last.dist_to(pl.last_pt())\n                        } else {\n                            // The orientation doesn't matter\n                            true\n                        }\n                    }\n                    CommonEndpoint::None => bail!(\n                        \"{} and {} don't share a common endpoint\",\n                        lane1.id,\n                        lane2.id\n                    ),\n                }\n            };\n            if !keep_lane_orientation {\n                pl = pl.reversed();\n            }\n\n            // Before we add this road's points, try to trace along the polygon's boundary. Usually\n            // this has no effect (we'll dedupe points), but sometimes there's an extra curve.\n            //\n            // Note this logic is similar to how we find SharedSidewalkCorners. Don't rely on that\n            // existing, since the outermost lane mightn't be a sidewalk.\n            //\n            // If the ring.doubles_back(), don't bother. If we tried to trace the boundary, it\n            // usually breaks the final Ring we produce. Better to skip bad intersection polygons","sourceCodeStart":626,"sourceCodeEnd":662,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/blockfinding/src/lib.rs#L626-L662","documentation":"to_block converts a perimeter into a Block by tracing its boundary lanes; it needs each consecutive pair of lanes to share a common endpoint to stitch the polylines together. When CommonEndpoint::None, the two lanes are disjoint, so the block cannot be built and this error names both lane IDs.","triggerScenarios":"Calling Perimeter::to_block (public API) on a perimeter whose step trace encounters two adjacent lanes with no shared endpoint, during block conversion.","commonSituations":"Calling to_block on a perimeter you built by hand instead of via blockfinding; map edits that disconnected lanes after blocks were found; side-of-driving or step-direction logic misidentifying the shared corner.","solutions":["Verify the two lane IDs from the message actually meet at a shared intersection in the current map.","Build the Perimeter through the normal blockfinding pipeline rather than hand-constructing it.","Re-run find_blocks on the latest map to refresh cached blocks after road edits.","If maintaining the code, ensure the correct CommonEndpoint variant (FirstFirst/FirstLast/...) is handled so orientation is fixed before tracing."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if perimeter.roads.windows(2).any(|w| {\n    map.get_r(w[0].road).common_endpoint(map.get_r(w[1].road)) == CommonEndpoint::None\n}) {\n    return Err(anyhow!(\"perimeter has disjoint adjacent roads\"));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Construct perimeters via the blockfinding pipeline, not by hand","Refresh blocks after road network changes","Validate all adjacent lane pairs share an endpoint before to_block"],"tags":["blocks","lanes","geometry"],"backgroundTag":"invalid-state-transition","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}