GTFS validation rules
The 178 rules of the MobilityData validator applied by GTFS Express, with what each one checks.
Errors (117)
bidirectional_exit_gateDetailed page- A pathway with pathway_mode=7 (exit gate) declares is_bidirectional=1: exit gates are unidirectional by definition.
block_trips_with_overlapping_stop_timesDetailed page- Two trips sharing the same block_id have overlapping stop_time intervals on a common service day: physically impossible vehicle chaining.
csv_parsing_failedDetailed page- A CSV file could not be parsed: malformed quoting, encoding error, or I/O failure.
decreasing_or_equal_shape_pt_sequenceDetailed page- Within a shape, shape_pt_sequence does not strictly increase across consecutive rows after sorting.
decreasing_or_equal_stop_sequenceDetailed page- Within a trip, stop_sequence does not strictly increase across consecutive rows after sorting.
decreasing_or_equal_stop_time_distanceDetailed page- shape_dist_traveled is not strictly increasing between two consecutive stop_times of the same trip.
decreasing_shape_distanceDetailed page- shape_dist_traveled decreases across consecutive shape points.
decreasing_stop_timeDetailed page- A stop_times arrival_time is earlier than the previous departure_time on the same trip: time travel.
duplicate_geo_json_keyDetailed page- A feature id is repeated within the same locations.geojson file: feature ids must be unique.
duplicate_geography_idDetailed page- A locations.geojson feature id collides with a stop_id (or location_group_id): geography ids must be unique across stops.txt, locations.geojson and location_groups.txt.
duplicate_keyDetailed page- Primary-key column has duplicate values (agency_id, stop_id, route_id, trip_id, service_id, …).
duplicated_columnDetailed page- Two columns in the same CSV file share the same name.
empty_column_nameDetailed page- A CSV header contains an empty cell: column name is missing.
empty_fileDetailed page- A GTFS file has zero data rows (header only, or completely empty).
equal_shape_distance_diff_coordinatesDetailed page- Two consecutive points share the same shape_dist_traveled but different coordinates: distance metric is inconsistent.
fare_product_with_multiple_default_rider_categoriesDetailed page- A fare_product references more than one rider category with is_default_fare_category=1: at most one default rider category is allowed per product.
fare_transfer_rule_duration_limit_type_without_duration_limitDetailed page- A fare_transfer_rules row sets duration_limit_type without duration_limit: both fields are conditionally required together.
fare_transfer_rule_duration_limit_without_typeDetailed page- A fare_transfer_rules row sets duration_limit without duration_limit_type: both fields are conditionally required together.
fare_transfer_rule_invalid_transfer_countDetailed page- A fare_transfer_rules.transfer_count is not -1 (unlimited) or a positive integer.
fare_transfer_rule_missing_transfer_countDetailed page- A fare_transfer_rules row has from_leg_group_id equal to to_leg_group_id but no transfer_count: required for symmetric transfers.
fare_transfer_rule_with_forbidden_transfer_countDetailed page- A fare_transfer_rules row sets transfer_count when from_leg_group_id differs from to_leg_group_id: transfer_count is forbidden for asymmetric transfers.
forbidden_arrival_or_departure_timeDetailed page- A Flex stop_times row that targets a location_id or location_group_id sets arrival_time / departure_time. These columns are forbidden when the trip serves a zone or stop group instead of a fixed stop.
forbidden_continuous_pickup_drop_offDetailed page- continuous_pickup or continuous_drop_off ≠ 1 used together with window fields: conditionally forbidden.
forbidden_drop_off_typeDetailed page- drop_off_type=0 used together with window fields: conditionally forbidden.
forbidden_geography_idDetailed page- Catalogue-only entry for canonical-tool compatibility (semantics deferred).
forbidden_pickup_typeDetailed page- pickup_type=0 or 3 (regular / coordinate-with-driver) used together with start/end_pickup_drop_off_window: these are conditionally forbidden.
forbidden_prior_day_booking_field_valueDetailed page- A booking_rules.txt row with booking_type=2 (prior-day notice) carries a prior_notice_duration_min/max field reserved for the same-day flow.
forbidden_prior_notice_start_dayDetailed page- A booking_rules.txt row carries prior_notice_start_day on a booking_type that does not allow it.
forbidden_prior_notice_start_timeDetailed page- A booking_rules.txt row carries prior_notice_start_time on a booking_type that does not allow it.
forbidden_real_time_booking_field_valueDetailed page- A booking_rules.txt row with booking_type=0 (real-time) carries a prior_notice_* field. Real-time booking rules accept no advance-notice configuration.
forbidden_same_day_booking_field_valueDetailed page- A booking_rules.txt row with booking_type=1 (same-day notice) carries a prior_notice_last_* or prior_notice_start_* field reserved for the prior-day flow.
forbidden_shape_dist_traveledDetailed page- A stop_times row sets shape_dist_traveled but the parent trip declares no shape_id (or shapes.txt is empty).
foreign_key_violationDetailed page- A reference to another file's primary key cannot be resolved (route_id → routes, service_id → calendar/calendar_dates, agency_id → agency, …).
geo_json_duplicated_elementDetailed page- A feature property is declared more than once (case-insensitive collision): only the last occurrence is kept by JSON parsers.
inconsistent_agency_timezoneDetailed page- Multiple agencies declare different timezones: the spec mandates a single timezone across all agencies in a feed.
invalid_boolean_valueDetailed page- A boolean field (calendar weekday columns, exact_times, is_bidirectional, …) has a value other than 0 or 1.
invalid_colorDetailed page- A color field is not a 6-digit uppercase or lowercase hex string (route_color, route_text_color).
invalid_currencyDetailed page- A currency field does not contain a valid ISO 4217 alpha-3 currency code (e.g. USD, EUR, JPY).
invalid_currency_amountDetailed page- A currency-amount field is malformed or has more decimal places than the ISO 4217 minor-unit precision allows for the declared currency (e.g. 1.234 USD, 100.5 JPY).
invalid_dateDetailed page- A date field is not in YYYYMMDD format.
invalid_exact_timesDetailed page- exact_times is not 0 or 1.
invalid_feed_end_dateDetailed page- feed_end_date is not in YYYYMMDD format.
invalid_feed_start_dateDetailed page- feed_start_date is not in YYYYMMDD format.
invalid_geometryDetailed page- A polygon or multipolygon in locations.geojson has malformed coordinates (non-finite values, points out of lon/lat range, ring shorter than 4 points, or unclosed ring).
invalid_headwayDetailed page- headway_secs is not a positive integer.
invalid_language_codeDetailed page- A language field does not match a pragmatic BCP 47 pattern (full IANA registry lookup is not enforced).
invalid_phone_numberDetailed page- A phone number field does not parse as a valid international or country-scoped phone number.
invalid_pickup_drop_off_windowDetailed page- start_pickup_drop_off_window is greater than or equal to end_pickup_drop_off_window on a Flex stop_times row.
invalid_prior_notice_duration_minDetailed page- prior_notice_duration_min is not a non-negative integer, or it is greater than prior_notice_duration_max.
invalid_row_lengthDetailed page- A data row's column count does not match the header column count.
invalid_shape_latDetailed page- shape_pt_lat is missing or not in [-90, 90].
invalid_shape_lonDetailed page- shape_pt_lon is missing or not in [-180, 180].
invalid_timeDetailed page- A time field does not match the GTFS HH:MM:SS format (hours may exceed 24:00:00 to encode service-day overflow).
invalid_timezoneDetailed page- A timezone field is not a valid IANA tz database identifier (validated via Intl.DateTimeFormat).
invalid_transfer_typeDetailed page- transfer_type is not 0-5.
invalid_urlDetailed page- A URL field does not parse as an absolute http(s) URL.
location_with_unexpected_stop_timeDetailed page- A Flex stop_times row sets BOTH stop_id and (location_id or location_group_id): exactly one must be set.
location_without_parent_stationDetailed page- A location_type=2/3/4 (entrance, generic node, boarding area) has no parent_station: these types must be attached to a station.
malformed_jsonDetailed page- locations.geojson cannot be parsed as JSON: syntactically invalid file.
missing_calendar_and_calendar_date_filesDetailed page- Both calendar.txt and calendar_dates.txt are absent: service definitions are required.
missing_feed_info_langDetailed page- feed_info.feed_lang is empty: required to disambiguate translated string fields.
missing_feed_info_publisher_nameDetailed page- feed_info.feed_publisher_name is empty: required for traceability and open-data portals.
missing_feed_info_publisher_urlDetailed page- feed_info.feed_publisher_url is empty: required for traceability and open-data portals.
missing_from_stop_idDetailed page- transfers.txt row is missing from_stop_id.
missing_level_idDetailed page- A pathway or stop references a level_id that is absent from levels.txt.
missing_min_transfer_timeDetailed page- transfer_type=2 (min-time transfer) but min_transfer_time is empty.
missing_pickup_or_drop_off_windowDetailed page- A Flex stop_times row referencing a location_id or location_group_id is missing start_pickup_drop_off_window or end_pickup_drop_off_window.
missing_prior_day_booking_field_valueDetailed page- A booking_rules.txt row with booking_type=2 (prior-day notice) is missing a required prior_notice_last_day or prior_notice_last_time field.
missing_prior_notice_duration_minDetailed page- A booking_rules.txt row with booking_type=1 (same-day notice) does not declare prior_notice_duration_min: required for the same-day flow.
missing_prior_notice_last_dayDetailed page- A booking_rules.txt row with booking_type=2 (prior-day notice) does not declare prior_notice_last_day.
missing_prior_notice_last_timeDetailed page- A booking_rules.txt row with booking_type=2 (prior-day notice) does not declare prior_notice_last_time.
missing_prior_notice_start_timeDetailed page- A booking_rules.txt row carries prior_notice_start_day without the matching prior_notice_start_time.
missing_required_columnDetailed page- A required column is absent from a present GTFS file's header.
missing_required_fieldDetailed page- A required field is empty on a row where the spec mandates a value (e.g. agency_name, stop_id, trip_id, route_id, stop_sequence).
missing_required_fileDetailed page- A GTFS file declared mandatory by the spec (agency, routes, stops, trips, stop_times, calendar/calendar_dates) is absent.
missing_stop_nameDetailed page- stop_name is empty on a stop where the spec requires it (location_type 0, 1 or 2).
missing_stop_times_recordDetailed page- A trip declared in trips.txt has zero rows in stop_times.txt.
missing_to_stop_idDetailed page- transfers.txt row is missing to_stop_id.
missing_trip_edgeDetailed page- A trip's first or last stop_times row is missing arrival_time AND departure_time: endpoints must be timed.
new_line_in_valueDetailed page- A non-quoted/un-escaped newline is embedded inside a CSV value, which would corrupt parsing in many downstream tools.
number_out_of_rangeDetailed page- A numeric/enum field is outside its spec-mandated range (pickup_type, drop_off_type, continuous_*, timepoint, direction_id, wheelchair_*, bikes_allowed, ...).
overlapping_frequencyDetailed page- Two frequencies.txt rows for the same trip have overlapping [start_time, end_time) intervals.
overlapping_zone_and_pickup_drop_off_windowDetailed page- Two Flex stop_times rows on the same trip target the same zone (location_id) within overlapping pickup/drop-off windows.
pathway_to_platform_with_boarding_areasDetailed page- A pathway points to a platform that has child boarding areas: pathway must reach the boarding area, not its parent.
pathway_to_wrong_location_typeDetailed page- A pathway endpoint references a stop with an incompatible location_type.
pathway_unreachable_locationDetailed page- A station-bound location node has no pathway leading to or from it.
point_near_originDetailed page- A stop is within ~10 m of (0, 0): Null Island, almost certainly a missing or zeroed coordinate.
point_near_poleDetailed page- A stop or shape point sits within 1° of either geographic pole (|lat| > 89.0°): almost certainly a coordinate-entry typo.
prior_notice_last_day_after_start_dayDetailed page- prior_notice_last_day is strictly greater than prior_notice_start_day on a booking_rules.txt row.
route_both_short_and_long_name_missingDetailed page- Neither route_short_name nor route_long_name is provided: at least one is required.
route_networks_specified_in_more_than_one_fileDetailed page- A route_id has both a non-empty routes.network_id AND an entry in route_networks.txt. The spec requires exactly one source.
service_without_service_daysDetailed page- A calendar.txt service has all weekday columns set to 0 AND no compensating positive calendar_dates exception: the service never runs.
start_and_end_date_out_of_orderDetailed page- A start_date is greater than its corresponding end_date (calendar.txt or feed_info.txt).
start_and_end_range_equalDetailed page- A frequencies.txt or timeframes.txt row sets start_time == end_time, defining a zero-duration window. Likely a typo.
start_and_end_range_out_of_orderDetailed page- A start_time is greater than or equal to its corresponding end_time on a frequencies.txt row.
station_with_parent_stationDetailed page- A location_type=1 (station) declares a parent_station: stations are top-level by spec.
stop_lat_out_of_rangeDetailed page- stop_lat is not a number in [-90, 90].
stop_lon_out_of_rangeDetailed page- stop_lon is not a number in [-180, 180].
stop_time_timepoint_without_timesDetailed page- A row with timepoint=1 (or empty, defaulting to exact) has neither arrival_time nor departure_time.
stop_time_with_arrival_before_previous_departure_timeDetailed page- On the same trip, the arrival_time of a stop_time row is strictly less than the previous row's departure_time.
stop_time_with_departure_before_arrival_timeDetailed page- On a single stop_times row, departure_time is strictly less than arrival_time.
stop_time_with_only_arrival_or_departure_timeDetailed page- Exactly one of arrival_time / departure_time is provided: the spec requires both or neither.
stop_without_locationDetailed page- A location_type=0/1/2 stop is missing stop_lat or stop_lon.
timeframe_only_start_or_end_time_specifiedDetailed page- A timeframes.txt row sets only one of (start_time, end_time). Both fields must be either both present or both absent.
timeframe_overlapDetailed page- Two timeframes.txt rows in the same timeframe_group_id define overlapping (start_time, end_time) windows for the same service_id.
timeframe_start_or_end_time_greater_than_twenty_four_hoursDetailed page- A timeframes.txt start_time or end_time is greater than 24:00:00. Unlike stop_times, timeframes are not allowed to spill past the service-day boundary.
transfer_with_invalid_stop_location_typeDetailed page- A transfers.txt row references a stop whose location_type is not platform (0) or station (1).
transfer_with_invalid_trip_and_routeDetailed page- A transfers.txt row sets both trip_id and route_id, but the trip's actual route_id does not match.
transfer_with_invalid_trip_and_stopDetailed page- A transfers.txt row sets both trip_id and stop_id, but the stop is not referenced by any stop_times row of the trip.
translation_foreign_key_violationDetailed page- A translation row's record_id does not exist in the referenced table_name.
translation_unexpected_valueDetailed page- A translation row references a field_value that does not match the source field: translation key cannot be applied.
trip_distance_exceeds_shape_distanceDetailed page- max(stop_times.shape_dist_traveled) on a trip is greater than the haversine length of its shape polyline (over 1 % tolerance).
trip_without_stop_timesDetailed page- A trip has no stop_times row: orphan trip.
unsupported_feature_typeDetailed page- An object inside locations.geojson features[] does not declare type='Feature'.
unsupported_geo_json_typeDetailed page- locations.geojson top-level type is not 'FeatureCollection': GTFS-Flex requires the file to wrap features in a FeatureCollection.
unsupported_geometry_typeDetailed page- A locations.geojson feature uses a geometry type other than Polygon or MultiPolygon: GTFS-Flex only supports those two.
wrong_parent_location_typeDetailed page- A parent_station references a stop whose location_type is not 1 (station).
Warnings (54)
attribution_without_roleDetailed page- An attributions.txt row sets none of is_producer / is_operator / is_authority to 1.
duplicate_fare_mediaDetailed page- Two distinct fare_media rows share the same (fare_media_name, fare_media_type) tuple: downstream consumers cannot tell them apart.
duplicate_route_nameDetailed page- Two distinct routes share both short_name and long_name: likely a data-entry duplicate.
empty_rowDetailed page- A CSV row contains only commas (every field empty). Treated as a non-data row and skipped, but signals a likely export bug upstream.
equal_shape_distance_diff_coordinates_distance_below_thresholdDetailed page- Two consecutive shape points share shape_dist_traveled and the geographic delta is below 1.11 m: negligible impact.
equal_shape_distance_same_coordinatesDetailed page- Two consecutive points share both shape_dist_traveled and coordinates: duplicate point.
expired_calendarDetailed page- An individual calendar.txt service's end_date is in the past.
fast_travel_between_consecutive_stopsDetailed page- Two consecutive stop_times rows imply an average speed greater than the route_type's plausible upper bound (e.g. >150 km/h on a bus). Likely a missing stop or a clock error.
fast_travel_between_far_stopsDetailed page- A trip's overall average speed (Σ distance / Σ time) exceeds a high threshold even when consecutive segments individually look plausible. Often signals a service discontinuity.
feed_expiration_date_30_daysDetailed page- feed_end_date is within 30 days from now.
feed_expiration_date_7_daysDetailed page- feed_end_date is within 7 days from now: heads-up for publishers.
feed_expiredDetailed page- The feed-level end_date is in the past.
feed_info_lang_and_agency_lang_mismatchDetailed page- feed_info.feed_lang differs from the dominant agency.agency_lang.
inconsistent_agency_langDetailed page- Multiple agencies declare different languages: interoperability hint.
leading_or_trailing_whitespacesDetailed page- A CSV value protected by double quotes carries leading or trailing whitespace. Quoted whitespace risks breaking key/FK matches when the same id appears with and without padding in different files.
missing_bike_allowanceDetailed page- trips.bikes_allowed is empty: recommended for active-mobility consumers.
missing_feed_contact_email_and_urlDetailed page- feed_info.txt declares neither feed_contact_email nor feed_contact_url. Consumers cannot reach the publisher to report data issues.
missing_feed_info_dateDetailed page- Exactly one of feed_start_date / feed_end_date is provided: both should be supplied together.
missing_pickup_drop_off_booking_rule_idDetailed page- A stop_times row uses pickup_type=2 or drop_off_type=2 (must phone agency) but does not reference a pickup_booking_rule_id / drop_off_booking_rule_id.
missing_recommended_fieldDetailed page- A recommended (but not required) field is absent from a row that has the corresponding column declared.
missing_recommended_fileDetailed page- A non-mandatory but strongly recommended file (feed_info.txt) is absent.
missing_timepoint_valueDetailed page- stop_times.timepoint is empty: recommended to set 0 (inexact) or 1 (exact) explicitly.
mixed_case_recommended_fieldDetailed page- A textual field uses inconsistent casing (e.g. ALL CAPS for stop_name) reducing readability for end users. MobilityData recommends Title Case for human-facing names.
more_than_one_entityDetailed page- feed_info.txt contains more than one row: the spec mandates exactly one.
non_ascii_or_non_printable_charDetailed page- A field contains non-printable control characters (tab, vertical tab, form feed, etc.) or non-ASCII characters that may not survive ASCII-only consumers.
pathway_dangling_generic_nodeDetailed page- A generic_node (location_type=3) has no boarding-area child or no pathway connection: disconnected node.
pathway_loopDetailed page- from_stop_id and to_stop_id of a pathway are identical: self-loop.
route_color_contrastDetailed page- WCAG sRGB contrast ratio between route_color and route_text_color is below 4.5:1, accessibility concern.
route_long_name_contains_short_nameDetailed page- route_long_name token-includes route_short_name (e.g. short=42, long="Bus 42 Centre"): redundant for riders.
route_short_name_too_longDetailed page- route_short_name exceeds 12 characters: short names should fit on signage and timetables.
route_type_invalidDetailed page- route_type is not in the GTFS spec's enum (0-12 plus extended 100-1799 hierarchical types).
route_without_tripsDetailed page- A route has no trips assigned to it.
same_name_and_description_for_routeDetailed page- route_desc duplicates route_short_name or route_long_name.
same_name_and_description_for_stopDetailed page- stop_desc is identical to stop_name: the description should add information, not repeat the name.
same_route_and_agency_urlDetailed page- A route's route_url equals its agency's agency_url. Likely a default-fill bug.
same_stop_and_agency_urlDetailed page- A stop's stop_url equals an agency's agency_url. Likely a default-fill bug.
same_stop_and_route_urlDetailed page- A stop's stop_url equals some route's route_url. Likely a default-fill bug.
single_shape_pointDetailed page- A shape_id has exactly one shape_pt_sequence row. A shape needs at least two points to define a segment.
stop_has_too_many_matches_for_shapeDetailed page- More than 5 shape vertices are within the matching threshold of a single stop: ambiguous projection.
stop_time_travel_distance_too_longDetailed page- Distance between two consecutive stops on a trip exceeds 500 km (sanity threshold).
stop_time_travel_speed_too_fastDetailed page- Computed travel speed between consecutive stops exceeds the per-route_type ceiling (100-300 km/h).
stop_too_far_from_shapeDetailed page- A stop is more than 100 m from the nearest vertex of its trip's shape polyline.
stop_too_far_from_shape_using_user_distanceDetailed page- Variant of stop_too_far_from_shape using shape_dist_traveled: the predicted shape-position is far from the stop's actual coordinates.
stop_without_stop_timeDetailed page- A stop is never referenced by any stop_times row: unused boarding location.
stops_match_shape_out_of_orderDetailed page- The stops of a trip, projected onto its shape polyline, are not in monotonic increasing order.
transfer_distance_too_largeDetailed page- Distance between from_stop and to_stop exceeds 2 km: unrealistic walking transfer.
transfer_with_suspicious_mid_trip_in_seatDetailed page- transfer_type=4 (in-seat continuation) between two trips without a shared block_id: likely modelling error.
translation_unknown_table_nameDetailed page- translations.table_name is not a recognised GTFS table.
trip_coverage_not_active_for_next7_daysDetailed page- Trip's service_id is inactive for the next 7 days: passengers cannot board it during that window.
trip_distance_exceeds_shape_distance_below_thresholdDetailed page- Same condition as trip_distance_exceeds_shape_distance but below the strict tolerance: review recommended.
unusable_tripDetailed page- A trip has fewer than two stop_times rows. The trip cannot be travelled and will be ignored by routing engines.
unused_parent_stationDetailed page- Alias of unused_station emitted alongside it for canonical-tool compatibility.
unused_shapeDetailed page- A shape_id defined in shapes.txt is not referenced by any trip in trips.txt. The geometry is dead weight in the export ZIP.
unused_tripDetailed page- A trip's service_id is never active in any of the next 7 days from feed_start_date: the trip is currently unreachable.
Information (7)
geo_json_unknown_elementDetailed page- An element in locations.geojson is not recognised by the GTFS-Flex profile and will be ignored by canonical consumers.
platform_without_parent_stationDetailed page- A platform (location_type=0 or empty) has no parent_station. Recommended for wayfinding.
stop_without_zone_idDetailed page- A platform (location_type=0 or empty) has no zone_id. Recommended for feeds that use fare zoning.
transfer_distance_above_2_kmDetailed page- A transfers.txt row links two stops more than 2 km apart: INFO threshold complementary to the 10 km transfer_distance_too_large.
unknown_columnDetailed page- A column header in a recognised GTFS file is not declared by the spec for that file. The data in this column will be silently ignored by downstream consumers.
unknown_fileDetailed page- A file is present in the feed ZIP but is not part of the GTFS Schedule, GTFS-Flex, or DRT-Booking spec. It will be silently ignored by downstream consumers.
unused_stationDetailed page- A station (location_type=1) is not referenced as parent_station by any platform/stop. Stations exist purely to group child stops.