Build a routing service: troubleshooting guide

A practical troubleshooting guide for routing services that convert geographic coordinates into paths, distances, durations, turn-by-turn instructions, matrices, or isochrones. It focuses on isolating failures in input validation, map matching, network coverage, routing profiles, API behavior, spatial calculations, and deployment rather than treating every incorrect route as an algorithm problem.

A routing service sits between geographic data and an application’s decision-making layer. It must interpret coordinates, connect them to a routable network, apply a travel mode or costing profile, calculate a path, and return results in a form that downstream systems can trust. When a request fails, the visible symptom may be simple—an HTTP error, no route, a large detour, or an implausible duration—but the cause can be anywhere from reversed coordinate order to stale OpenStreetMap extracts, an unsuitable profile, incorrect units, or a route that is technically valid but does not match the application’s operational meaning. This guide presents a provider-neutral diagnostic workflow, with concrete examples from documented interfaces such as OSRM’s HTTP API, Valhalla’s JSON route service, GraphHopper’s Routing API, and PostGIS spatial functions. It is intended for teams running a self-hosted engine or integrating a managed routing provider. The examples are deliberately explicit about what each routing engine does and does not guarantee.

How EDK fits

For: Backend engineers, GIS developers, data engineers, platform teams, and geospatial analysts troubleshooting a road, pedestrian, bicycle, multimodal, matrix, or isochrone service.

Routing is one of the clearest examples of computing about the physical world. A route is not merely a line between two points: it is a computation over roads, paths, access rules, turn restrictions, travel modes, barriers, and place-specific context. A world-model perspective helps separate the routing graph from the broader geographic reality around it. The graph answers how movement is modeled; surrounding features—buildings, roads, waterways, service locations, public assets, construction activity, and environmental constraints—help explain whether the result is useful in the real world. E2’s relevance here is conceptual and editorial: routing is a specialized computation layer, while a broader physical-world representation can provide the geographic context needed to find, compare, validate, and act on routes. This guide does not claim that E2 supplies a routing engine, live traffic, turn-by-turn navigation, or any specific third-party routing dataset.

  • •Use a physical-world model to validate route endpoints against nearby roads, entrances, buildings, barriers, loading areas, or other geographic features before sending them to a routing engine. This can distinguish an address point, parcel centroid, building entrance, and actual drivable access point.
  • •Use route outputs as one computed layer alongside nearby physical features. For example, compare travel paths or travel-time matrices with schools, clinics, charging stations, hydrants, parks, service requests, construction permits, or road classes to understand operational coverage rather than looking only at route geometry.
Primary output
A path through a routable geographic network, commonly with distance, duration, geometry, legs, or turn instructions.
Common inputs
Coordinates, ordered waypoints, travel mode or costing profile, and provider-specific routing options.
Frequent failure classes
Invalid coordinates, wrong coordinate order, failed snapping, disconnected graph, unsuitable profile, malformed options, stale data, and response-decoding errors.
Relevant external engines
OSRM, Valhalla, and GraphHopper all document routing APIs, but their request shapes, profiles, preprocessing, and feature coverage differ.

The EDK outcome

Before you start

A reproducible routing request and the complete response body with credentials removed.

Basic familiarity with HTTP APIs, JSON, geographic coordinates, and GeoJSON.

Access to the routing engine’s documentation, configuration, logs, and network-data build information.

A known test location set covering connected roads, disconnected areas, restricted access, pedestrian or bicycle paths, and off-network points.

If using PostGIS, a database with geometry or geography columns, explicit SRIDs, and spatial indexes where appropriate.

Learning objectives

Classify a routing failure as an input, snapping, network, profile, API, geometry, or deployment problem.

Verify coordinate order, endpoint snapping, travel mode, route geometry, distance units, and matrix ordering.

Use documented OSRM, Valhalla, or GraphHopper interfaces without assuming their options are interchangeable.

Validate route outputs against surrounding geographic features and operational constraints.

Create a repeatable troubleshooting and regression-testing process for a routing service.

Verification

The same minimal request produces a stable result in the intended environment.

Requested coordinates and returned snapped waypoints are in the expected locations.

The selected profile or costing model permits the intended travel mode and network.

Distance, duration, geometry format, units, and matrix ordering are validated against the provider’s documentation.

A route that is technically valid has been checked against relevant physical context such as entrances, barriers, service areas, roads, buildings, or temporary restrictions.

The service exposes enough logs and metrics to distinguish client, API, graph, profile, and infrastructure failures.

Troubleshooting

If every request fails, test the process health endpoint or a known-good local route, then inspect logs, port binding, graph paths, profile loading, and recent deployments.

If only one area fails, compare network coverage, endpoint snapping, barriers, access tags, bridge or ferry connections, and data freshness in that area.

If routes are consistently too long, check whether the points are snapped to the wrong roads, whether the profile applies unexpected penalties, and whether one-way or access restrictions are being honored.

If durations are implausible, verify units, profile speeds, traffic assumptions, departure-time behavior, and whether the response is being interpreted as seconds or minutes.

If the route appears correct but downstream proximity results are wrong, inspect SRIDs, geometry-versus-geography types, degree-versus-meter assumptions, and spatial-index usage.

If production performance degrades, compare request size, waypoint count, alternatives, annotations, geometry verbosity, concurrency, cache behavior, and graph reload activity.

If a recently closed or changed road still appears, determine whether the engine uses static extracts, a custom overlay, live traffic, or another update mechanism. Do not infer freshness from the route response alone.

Next steps

Add golden routing tests to continuous integration using known coordinate pairs and expected route properties rather than brittle exact polylines.

Create a route-quality review process that combines network results with buildings, entrances, barriers, roads, public assets, and other physical-world context.

Choose a routing engine based on required modes, graph size, update cadence, optimization features, licensing, hosting model, and observability—not only benchmark latency.

Add explicit provenance to route results: provider, engine version, profile, network-data date, request parameters, and any applied overlays.

Read the related geocoder troubleshooting guide when endpoint coordinates originate from address search, and the GeoPandas troubleshooting guide when routes are analyzed with spatial joins or measurements.

What you need before debugging

Capture one failing request exactly as sent, including the routing provider, endpoint, profile or costing model, coordinates, options, HTTP status, response body, and timestamp. Remove credentials, but do not reduce the request to a vague description such as “routing is wrong.” A reproducible request is the smallest useful test case.

Record the source and age of the network data. OSRM responses may include a data_version when available, and Valhalla’s documentation identifies OpenStreetMap as the main graph source. If your application depends on closures, private roads, truck restrictions, ferries, pedestrian paths, or transit, record how those data are imported and how often they are refreshed. A routing engine cannot return constraints that are absent from its graph or profile.

  • •A reproducible request and response
  • •Known network-data source and refresh timestamp
  • •Declared coordinate order and CRS
  • •A test set covering normal, disconnected, restricted, and off-network points
  • •Metrics for latency, error rate, no-route rate, and route plausibility

Step 1: verify coordinates, snapping, and request shape

Start with the request itself. Check latitude ranges of -90 to 90 and longitude ranges of -180 to 180. Then verify coordinate order at every boundary: browser maps, GeoJSON, database columns, routing URL builders, and response parsers. OSRM’s documented URL format is longitude,latitude; Valhalla’s route payload uses separate lat and lon properties. A coordinate pair can be numerically valid while being in the wrong country if its order is reversed. (project-osrm.org)

Use a nearest or locate operation where the engine provides one, or inspect returned waypoints. OSRM exposes a Nearest service and returns waypoint locations; Valhalla provides a locate service for graph metadata. If the snapped point is hundreds of meters from the intended origin, the route problem may actually be an endpoint problem. The same check is important for addresses, building centroids, GPS observations, and map clicks.

  • •Check coordinate order before changing routing options.
  • •Compare requested coordinates with returned waypoint locations.
  • •Test one short, obvious route before testing a multi-stop route.
  • •Do not assume a building centroid is a valid vehicle access point.

Step 2: determine whether the network or profile is responsible

If endpoints snap correctly but the route is missing or implausible, isolate the network and the travel profile. Try the same coordinates with a different mode only when the engine and dataset support that mode. A car profile may reject a footpath; a bicycle profile may prefer cycleways; a pedestrian profile may use paths unavailable to cars. Valhalla describes route requests in terms of costing models such as auto, bicycle, and pedestrian, while OSRM profiles are selected during data preparation and determine the mode of transportation. (github.com)

Inspect the map data around the failure. Look for disconnected ways, missing bridges or ferries, incorrect one-way tags, access restrictions, private roads, barriers, or a destination represented as a point away from the entrance. If the route crosses a feature that should block travel, the issue may be data quality rather than search behavior.

  • •Validate graph connectivity around both endpoints.
  • •Check road direction, access, barriers, bridges, ferries, and private ways.
  • •Confirm the requested mode exists in the imported graph and profile.
  • •Separate no-route failures from routes that are valid but operationally undesirable.

Step 3: isolate API, geometry, and response failures

A routing engine can calculate a correct route while the application displays or stores it incorrectly. Compare the raw response with the parsed object used by the application. Check whether the response contains distance, duration, legs, steps, annotations, and geometry in the format requested. OSRM supports GeoJSON, polyline, and polyline6 geometries, and its route response includes distance and duration fields. GraphHopper documents route responses that can include path coordinates, turn instructions, elevation, and path details. (project-osrm.org)

A blank map may indicate a geometry-decoding or rendering problem, not a routing failure. Log the number of coordinates, the first and last coordinate, the geometry type, and the bounding box. Verify that GeoJSON uses [longitude, latitude] coordinate arrays. If you request encoded polylines, confirm the precision and decoder used by the client.

  • •Log the raw response before application parsing.
  • •Verify GeoJSON coordinate order and encoded-polyline precision.
  • •Check matrix row and column ordering.
  • •Make distance units and SRIDs explicit in spatial post-processing.

Step 4: troubleshoot deployment, performance, and freshness

Once a single request works, test the service under realistic load. Separate routing computation time from network latency, request parsing, response serialization, and client rendering. Record p50, p95, and p99 latency by request type. A route with many waypoints, alternatives, steps, annotations, or a large geometry can have a different cost from a simple two-point request.

Check request-size limits and timeouts. OSRM documents service-specific restrictions and error codes such as TooBig, NoSegment, and NoRoute. Valhalla’s API documentation describes JSON requests and response errors, including rate limiting in its OpenAPI description. Treat HTTP success with an empty or unsuitable route as a separate application-level failure. (project-osrm.org)

  • •Measure routing latency separately from application latency.
  • •Watch for large waypoint lists and verbose geometry options.
  • •Verify graph build, profile, data directory, and process reloads.
  • •Expose data freshness and temporary-restriction limitations to downstream users.

Workflow

01

1. Capture a minimal reproducible request

Save the provider, endpoint, profile or costing model, coordinates, options, timestamp, HTTP status, and raw response. Reduce the failing case to two points if possible, then add waypoints or output options one at a time.

curl -sS 'http://router.project-osrm.org/route/v1/driving/-73.9857,40.7484;-73.9772,40.7527?overview=full&geometries=geojson&steps=false' | jq .
02

2. Validate coordinate order and endpoint snapping

Check coordinate ranges, inspect returned waypoints, and compare the snapped locations with the intended road, entrance, or facility. For Valhalla, use separate lat and lon fields; for OSRM URL requests, use longitude,latitude pairs.

curl -sS 'http://router.project-osrm.org/nearest/v1/driving/-73.9857,40.7484' | jq '{code,waypoints}'
03

3. Test the network and profile separately

Run a short known-good route with the same profile. Then test the failing location with the documented mode or costing model. Inspect barriers, one-way streets, access restrictions, disconnected ways, bridges, ferries, and missing paths.

curl -sS -X POST 'http://localhost:8002/route' -H 'content-type: application/json' -d '{"locations":[{"lat":40.7484,"lon":-73.9857},{"lat":40.7527,"lon":-73.9772}],"costing":"auto","directions_options":{"units":"miles"}}' | jq .
04

4. Compare raw and parsed geometry

Log the geometry type, coordinate count, first and last coordinate, and bounding box before and after client parsing. Confirm polyline precision or GeoJSON order and verify that the map renderer expects the same representation.

jq '{code, routes: [.routes[] | {distance,duration,geometry,legs: (.legs|length)}]}' route-response.json
05

5. Validate spatial post-processing

When joining routes to nearby features, use a compatible CRS and explicit units. For meter-based proximity with PostGIS geography, use ST_DWithin and an appropriate spatial index rather than buffering every route or comparing raw degree values.

SELECT f.id
FROM physical_features AS f
WHERE ST_DWithin(
  f.geog,
  ST_SetSRID(ST_Point(-73.9857, 40.7484), 4326)::geography,
  250
);
06

6. Load-test and document freshness

Measure latency by request type, monitor errors and no-route rates, test large waypoint lists separately, verify graph-build and reload procedures, and record the source-data timestamp. Treat static-network routing and live traffic or closures as different capabilities unless the provider documents otherwise.

# Example metric dimensions to record
# route_mode, waypoint_count, geometry_format, http_status, provider_code, data_version

Questions teams ask

Why does my routing service return a huge detour when the points look close together?

First check coordinate order, snapping, and travel mode. A point may be on the wrong side of a river, separated by a one-way system, placed inside a building or parking area, or snapped to a different road than intended. Compare the requested coordinates with returned waypoints and inspect barriers, bridges, access tags, and graph connectivity before changing the algorithm.

Why does the API return no route even though a map shows a nearby road?

The visible road may not be connected to the selected profile, may be private or restricted, may be pedestrian-only, or may be absent from the routing graph. Test a short route on a known connected road, inspect the endpoint snap, and compare the requested mode with the engine’s documented profile or costing model. OSRM documents NoSegment and NoRoute-style outcomes, while Valhalla requires locations and a compatible costing model. (project-osrm.org)

Why is my route geometry correct in the API but wrong on the map?

Inspect the raw geometry and the client transformation. Confirm whether the response is GeoJSON, polyline, or polyline6; verify decoding precision; preserve longitude-latitude order for GeoJSON; and check that the map and stored geometry use the intended CRS. Log the first coordinate, last coordinate, coordinate count, and bounding box at each stage.

Should I use straight-line distance to validate a route?

Straight-line distance is useful as a rough sanity check, but it is not a substitute for network distance. Use the route’s reported distance and duration for travel calculations, and use spatial functions such as PostGIS ST_DWithin when you need indexed geographic proximity tests. Ensure that geometry units or geography meters match the business rule. (postgis.net)

Keep exploring

Sources

More in Guides