Skip to content

fix: raise SerpApiException on an error in a 200 response body - #6

Merged
jvmvik merged 1 commit into
masterfrom
fix/raise-on-body-level-error
Aug 16, 2026
Merged

fix: raise SerpApiException on an error in a 200 response body#6
jvmvik merged 1 commit into
masterfrom
fix/raise-on-body-level-error

Conversation

@jvmvik

@jvmvik jvmvik commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Problem

SerpApi reports some failures inside the body of an HTTP 200 response. The google_events engine is currently doing exactly this:

{
  "search_metadata": { "status": "Success" },
  "search_information": { "events_results_state": "Fully empty" },
  "error": "Google hasn't returned any results for this query."
}

HTTP 200, status: "Success", and an error field.

SerpApiHttp.get() decided success purely from the status code, so triggerSerpApiException was never reached and search() returned an object that is semantically an error. The caller then did the natural thing:

JsonObject results = client.search(parameter);
results.getAsJsonArray("events_results").size();   // NullPointerException

getAsJsonArray returns null for an absent key, so this NPEs — pointing at the caller's own line, with the actual explanation sitting unread in the error field. This is what broke GoogleEventsTest in CI, and every consumer of this library hits the same edge.

Change

Check for a body-level error in json() and location(), routing it through the existing triggerSerpApiException so a SerpApi error always reaches the caller as a SerpApiException, regardless of status code.

location() needs its own check because it bypasses json() and parses an array — an error body is an object, so it previously died on the cast instead.

Tests

New ErrorResponseTest stubs the HTTP client, so it runs offline and needs no SERPAPI_KEY. Verified non-vacuous: with the SerpApi.java change stashed, the three error-path tests fail and the two happy-path tests still pass.

Notes for review

  • This is a breaking behavior change. Code that currently gets a JsonObject back and inspects has("error") itself will now see an exception instead.
  • "Zero results" is arguably a legitimate empty outcome rather than an error, but SerpApi is the party calling it error, so honoring that label seems right.
  • html() still returns its raw String unchecked — parsing arbitrary HTML as JSON hunting for an error field isn't worth the risk.
  • Does not by itself turn CI green: HomeDepotTest is failing separately on a HttpTimeoutException (three consecutive runs), untouched here.

🤖 Generated with Claude Code

SerpApi reports some failures inside the body of an HTTP 200 response,
for example the google_events engine:

  HTTP 200
  {"search_metadata":{"status":"Success"},
   "search_information":{"events_results_state":"Fully empty"},
   "error":"Google hasn't returned any results for this query."}

The client decided success purely from the status code, so search()
returned an object that was semantically an error. Callers then reached
for the key they expected and got a NullPointerException pointing at
their own code, with the explanation sitting unread in the error field.
This is what broke GoogleEventsTest.

Check for a body-level error in json() and location(), routing it
through the existing triggerSerpApiException so every SerpApi error
reaches the caller as a SerpApiException regardless of status code.

html() still returns its raw String unchecked; parsing arbitrary HTML
as JSON to look for an error field is not worth the risk.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jvmvik
jvmvik merged commit 7913f2e into master Aug 16, 2026
1 check failed
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