Skip to content

Fixing common issues

A reference-by-symptom. Each entry starts with what you see in a validate run, then gives the one-liner that fixes it. Adapt the filters to your own feed — the patterns below are illustrative.

Symptom. fk_violation on stop_times.trip_id referencing trips that are no longer in trips.txt. This usually happens when trips were removed with an external script that does not understand referential integrity.

Fix. Re-run the delete through gapline so the cascade is applied:

Terminal window
gapline delete stop-times -f gtfs.zip \
--where "trip_id=GHOST_TRIP" \
--confirm

For a batch of orphaned rows, enumerate them first:

Terminal window
gapline read stop-times -f gtfs.zip --where "trip_id LIKE GHOST_%"

Then delete each affected trip_id. Going forward, perform the trip deletion with gapline delete trips — the cascade handles stop_times automatically.

Symptom. calendar_ranges error on a row whose end_date is in the past. Consumers drop the service silently; the feed remains structurally valid.

Fix. Either delete the expired services entirely:

Terminal window
gapline delete calendar -f gtfs.zip \
--where "end_date<20260101" \
--confirm

…or extend the range if the service is still active:

Terminal window
gapline update calendar -f gtfs.zip \
--where "service_id=WEEKDAY" \
--set end_date=20261231 \
--confirm

Symptom. fk_violation on trips.shape_id pointing at a shape_id that does not exist in shapes.txt.

Fix. If the shape should not be there, clear the reference:

Terminal window
gapline update trips -f gtfs.zip \
--where "shape_id=MISSING_SHAPE" \
--set shape_id="" \
--confirm

If the shape exists under a different ID, rewrite the reference:

Terminal window
gapline update trips -f gtfs.zip \
--where "shape_id=OLD_ID" \
--set shape_id=NEW_ID \
--confirm

Symptom. coordinates_near_origin or coordinates_near_pole warnings. Typically 0/0 placeholder coordinates left by an upstream script.

Fix. Find the offenders:

Terminal window
gapline read stops -f gtfs.zip --where "stop_lat=0 AND stop_lon=0"

Correct the values one by one:

Terminal window
gapline update stops -f gtfs.zip \
--where "stop_id=S_BROKEN" \
--set stop_lat=45.5017 stop_lon=-73.5673 \
--confirm

If the stops are unrecoverable, delete them — the cascade removes associated stop_times.

Symptom. primary_key_violation on stops.txt. Two or more rows share the same stop_id.

Fix. Read the duplicates to understand what differs between them:

Terminal window
gapline read stops -f gtfs.zip --where "stop_id=S_DUPE"

Typically one is the canonical stop and the others are duplicates of an upstream merge. Rename the duplicates with a unique suffix, using --cascade to keep dependent references consistent:

Terminal window
gapline update stops -f gtfs.zip \
--where "stop_id=S_DUPE AND stop_name LIKE %copy" \
--set stop_id=S_DUPE_ALT \
--cascade --confirm

Then re-validate; if the alternate stop is genuinely redundant, delete it:

Terminal window
gapline delete stops -f gtfs.zip --where "stop_id=S_DUPE_ALT"

The cascade preview lists every stop_times or transfers row that will be removed alongside the stop.

Symptom. speed_validation warning — the average speed between two consecutive stop_times exceeds the threshold for the route’s route_type.

Fix. Usually a typo in arrival_time or departure_time. Read the affected trip:

Terminal window
gapline read stop-times -f gtfs.zip --where "trip_id=T_FAST"

Correct the suspicious row:

Terminal window
gapline update stop-times -f gtfs.zip \
--where "trip_id=T_FAST AND stop_sequence=4" \
--set arrival_time=08:15:00 departure_time=08:15:00 \
--confirm

If the high speed is intentional (high-speed rail, for example), raise the threshold in gapline.toml instead:

[validation.thresholds.speed_limits]
rail_kmh = 320

Symptom. gapline refuses to load the feed and prints the offending file and byte offset.

Fix. GTFS mandates UTF-8. Transcode the offending file outside gapline and re-archive:

Terminal window
unzip gtfs.zip -d gtfs/
iconv -f latin1 -t utf8 gtfs/stops.txt -o gtfs/stops.utf8.txt
mv gtfs/stops.utf8.txt gtfs/stops.txt
(cd gtfs && zip -r ../gtfs.zip *.txt)

Then re-run gapline validate to confirm.