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.
Orphaned stop_times after a trip delete
Section titled “Orphaned stop_times after a trip delete”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:
gapline delete stop-times -f gtfs.zip \ --where "trip_id=GHOST_TRIP" \ --confirmFor a batch of orphaned rows, enumerate them first:
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.
Expired calendar.txt range
Section titled “Expired calendar.txt range”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:
gapline delete calendar -f gtfs.zip \ --where "end_date<20260101" \ --confirm…or extend the range if the service is still active:
gapline update calendar -f gtfs.zip \ --where "service_id=WEEKDAY" \ --set end_date=20261231 \ --confirmMissing shapes.txt references
Section titled “Missing shapes.txt references”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:
gapline update trips -f gtfs.zip \ --where "shape_id=MISSING_SHAPE" \ --set shape_id="" \ --confirmIf the shape exists under a different ID, rewrite the reference:
gapline update trips -f gtfs.zip \ --where "shape_id=OLD_ID" \ --set shape_id=NEW_ID \ --confirmCoordinates outside the valid range
Section titled “Coordinates outside the valid range”Symptom. coordinates_near_origin or coordinates_near_pole warnings. Typically 0/0 placeholder coordinates left by an upstream script.
Fix. Find the offenders:
gapline read stops -f gtfs.zip --where "stop_lat=0 AND stop_lon=0"Correct the values one by one:
gapline update stops -f gtfs.zip \ --where "stop_id=S_BROKEN" \ --set stop_lat=45.5017 stop_lon=-73.5673 \ --confirmIf the stops are unrecoverable, delete them — the cascade removes associated stop_times.
Duplicate stop_id values
Section titled “Duplicate stop_id values”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:
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:
gapline update stops -f gtfs.zip \ --where "stop_id=S_DUPE AND stop_name LIKE %copy" \ --set stop_id=S_DUPE_ALT \ --cascade --confirmThen re-validate; if the alternate stop is genuinely redundant, delete it:
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.
Unrealistic trip speeds
Section titled “Unrealistic trip speeds”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:
gapline read stop-times -f gtfs.zip --where "trip_id=T_FAST"Correct the suspicious row:
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 \ --confirmIf the high speed is intentional (high-speed rail, for example), raise the threshold in gapline.toml instead:
[validation.thresholds.speed_limits]rail_kmh = 320Non-UTF-8 encoding
Section titled “Non-UTF-8 encoding”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:
unzip gtfs.zip -d gtfs/iconv -f latin1 -t utf8 gtfs/stops.txt -o gtfs/stops.utf8.txtmv gtfs/stops.utf8.txt gtfs/stops.txt(cd gtfs && zip -r ../gtfs.zip *.txt)Then re-run gapline validate to confirm.
See also
Section titled “See also”- Concepts / Referential integrity — why the cascade happens.
- Concepts / Query language — writing precise
--wherefilters. - Guides / Editing with CRUD — the general read → update → delete loop.
- Validation rules — every rule ID referenced above.