Validating feeds
This guide walks through a realistic validation loop: pick the right output format, filter out noise, produce a report for a stakeholder, and iterate until the feed is clean.
1. Pick an output format
Section titled “1. Pick an output format”gapline validate can emit five formats. Match the format to the consumer.
| Format | Best for |
|---|---|
text | Interactive use. Colored by default; colors disable automatically on a non-TTY pipe. |
json | CI pipelines. Parseable with jq, diffable across runs. |
csv | Spreadsheet triage. One row per finding. |
xml | Legacy systems that already ingest GTFS validator XML. |
html | Share a report with a non-technical stakeholder. Self-contained, open by double-click. |
gapline validate -f gtfs.zip --format json -o report.jsongapline validate -f gtfs.zip --format html -o report.htmlgapline validate -f gtfs.zip --format csv -o report.csvThe full schema of each format is documented on output formats.
2. Filter by severity
Section titled “2. Filter by severity”By default, every finding is included. The exit code is non-zero only if any finding is at ERROR-level.
If the raw output is too noisy, use --min-severity to hide anything below a chosen level. This also reclassifies — a WARNING with --min-severity warning becomes gate-blocking.
# Fail the run on ERRORs only (default behavior).gapline validate -f gtfs.zip
# Also fail on WARNINGs.gapline validate -f gtfs.zip --min-severity warning
# Fail on anything, including INFO suggestions.gapline validate -f gtfs.zip --min-severity infoSee concepts / severities for the policy behind the three levels.
3. Disable noisy rules
Section titled “3. Disable noisy rules”Sometimes a rule is too strict for your feed. A typical example: block_id_trip_overlap fires when you intentionally chain two trips via a shared block_id. Disable it for this run:
gapline validate -f gtfs.zip --disable-rule block_id_trip_overlapSeveral IDs can follow a single --disable-rule:
gapline validate -f gtfs.zip --disable-rule block_id_trip_overlap duplicate_coordinatesFind the ID of a rule you want to silence with gapline rules list and a grep:
gapline rules list | grep -i speedFor a permanent silence, pin the list in gapline.toml:
[validation]disabled_rules = ["block_id_trip_overlap", "duplicate_coordinates"]--disable-rule on the CLI appends to the config list, it does not replace it. Shell completion suggests registered IDs dynamically — enable completion if you have not already.
4. Produce a report
Section titled “4. Produce a report”For yourself
Section titled “For yourself”gapline validate -f gtfs.zipColored text. Scan it, fix the errors, re-run.
For a CI pipeline
Section titled “For a CI pipeline”gapline validate -f gtfs.zip --format json -o report.jsonParse the JSON with jq to post comments on a pull request or to produce a diff versus the previous run:
jq '.summary' report.jsonjq '.errors | group_by(.rule_id) | map({rule: .[0].rule_id, count: length})' report.jsonFor a non-technical stakeholder
Section titled “For a non-technical stakeholder”gapline validate -f gtfs.zip --format html -o report.htmlThe HTML file is self-contained — CSS and JS are inlined. Email it, or host it somewhere static. No server needed.
5. Close the loop
Section titled “5. Close the loop”The goal is not to run validate once; it is to make running it part of the cadence that keeps your feed healthy.
#!/usr/bin/env bashif git diff --cached --name-only | grep -q '^data/gtfs/'; then gapline validate -f ./data/gtfs/ --min-severity error || exit 1fi- run: gapline validate -f data/gtfs.zip --format json -o report.json- uses: actions/upload-artifact@v4 with: { name: gtfs-report, path: report.json }curl -L -o feed.zip https://agency.example.com/gtfs.zipgapline validate -f feed.zip --format json -o "$(date +%F).json"See also
Section titled “See also”gapline validate— every flag, every exit code.- Concepts / Severities — when to use
--min-severity. - Output formats — JSON schema, CSV columns, XML root.
- Guides / CI integration — full pipeline recipes.