Skip to content

Fix segfault: guard nil OBA SDK responses (null JSON body)#3

Merged
aaronbrethorst merged 2 commits into
mainfrom
segfault
May 25, 2026
Merged

Fix segfault: guard nil OBA SDK responses (null JSON body)#3
aaronbrethorst merged 2 commits into
mainfrom
segfault

Conversation

@aaronbrethorst
Copy link
Copy Markdown
Member

Summary

  • Root cause: the OBA REST server returns a literal JSON null body with HTTP 200 for some queries (confirmed live on trips-for-location.json). The go-sdk decodes null into a **T, leaving the response (nil, nil) — a nil pointer with a nil error. Every check guarded only err != nil, then dereferenced resp.Data... → SIGSEGV. It was intermittent because the crashing entity depends on the live feed at fetch time.
  • Fix: add a nil-response guard to every in-check SDK call across check_vehicles.go, check_endpoints.go, check_alerts.go, check_tripupdates.go (the pre-fetched vc.Agencies was already guarded).
  • Severity follows the project's evidence-based model, matching each call's existing empty/error sibling: per-stop/per-vehicle cross-refs Warn (unconfirmed); vehicles-for-agency and the basic-endpoints smoke test Fail (the feed/endpoint proves data the API is missing).
  • Added withReason so a null-body Fail no longer renders a dangling colon ("current-time failed: ").

Test plan

  • go vet ./... clean, full go test ./... green
  • New tests reproduce the exact crash: each panics at the original deref line against pre-fix code (verified by reverting guards), then passes
    • vehicle check: trips-for-location / vehicles-for-agency / trip-for-vehicle null responses
    • endpoints: table test null-fuzzing all 6 SDK calls (distinct deref shapes), asserts Fail + no dangling colon
    • alerts & trip-updates: null arrivals → Warn (not the "absent" Fail)
  • Live run vs Puget Sound: originally-crashing oba-validator -json example_configs/kcm.json no longer panics; emitted the real warning trips-for-location returned a null response near vehicle "11743" in the wild
  • Genuine Fail detection still works (live run flagged a real trip-for-vehicle API-vs-feed trip mismatch → exit 1)

Review notes (deliberate decisions, flagging for visibility)

  • vehicles-for-agency null → Fail, but the other cross-ref null cases → Warn. Intentional: that call is the agency-wide roster whose empty-list branch already Fails, and a null is the same evidence (feed proves vehicles exist). The per-stop/per-vehicle cross-refs stay Warn because their empties are genuinely unconfirmed (wrong agency prefix, vehicle moved, no current trip).
  • Only the top-level response pointer is guarded. Verified against go-sdk v1.11.0 that all nested .Data/.Entry/.List are value types, so once the top-level pointer is non-nil the downstream derefs are safe.

🤖 Generated with Claude Code

The OBA REST server can return a literal JSON `null` body with HTTP 200.
The go-sdk decodes that into a (nil, nil) response — a nil pointer with a
nil error — so checks that dereferenced `resp.Data...` after only an
`err != nil` check panicked with SIGSEGV (reported on
`oba-validator -json example_configs/kcm.json`).

Add a nil-response guard to every in-check SDK call (vehicle, endpoint,
alert, trip-update checks). Severity follows the evidence-based model,
matching each call's existing empty/error sibling:
- per-stop/per-vehicle cross-refs Warn (unconfirmed)
- vehicles-for-agency Fails (feed proves vehicles exist, API returned none)
- the basic-endpoints smoke test Fails (null core endpoint = server fault)

Also add a `withReason` helper so a null-body Fail no longer renders a
dangling colon ("current-time failed: ").
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Warning

Review limit reached

@aaronbrethorst, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 3 minutes and 36 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d0f281de-9f7e-4167-b1e9-2b84b04958bd

📥 Commits

Reviewing files that changed from the base of the PR and between 1c08c69 and d0e0348.

📒 Files selected for processing (11)
  • validator/check_agencies.go
  • validator/check_alerts.go
  • validator/check_alerts_test.go
  • validator/check_endpoints.go
  • validator/check_endpoints_test.go
  • validator/check_tripupdates.go
  • validator/check_tripupdates_test.go
  • validator/check_vehicles.go
  • validator/check_vehicles_test.go
  • validator/helpers_test.go
  • validator/util.go
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch segfault

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

SonarQube flagged 24.5% duplication on new code. Remove the duplicated
blocks:

- Production: extract `queryArrivals`, shared by the alert and trip-update
  cross-reference checks, replacing the identical error/null-response
  guard each had inline.
- Tests: add shared helpers (`arrivalsClient`, `endpointsClient`,
  `vehicleClient`, `baseSrc`, `alertSrcForStop`, `tripUpdateSrcForStop`,
  `assertFirstStatus`), collapse the three vehicle null-response tests
  into one table test, and route the happy-path/empty/404 tests through
  the same builders instead of repeating httptest handlers and
  SourceContext literals.

Behavior unchanged; full suite green, `dupl -threshold 100` reports zero
clone groups in the package.
@sonarqubecloud
Copy link
Copy Markdown

@aaronbrethorst aaronbrethorst merged commit a23e1d8 into main May 25, 2026
6 checks passed
@aaronbrethorst aaronbrethorst deleted the segfault branch May 25, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant