Skip to content

Keep the HTTP status and response body in fallback errors - #10

Merged
mmaikel merged 1 commit into
masterfrom
mikolajstepniewski/http-status-on-errors
Oct 1, 2026
Merged

mmaikel merged 1 commit into
masterfrom
mikolajstepniewski/http-status-on-errors

Conversation

@mmaikel

@mmaikel mmaikel commented Oct 1, 2026

Copy link
Copy Markdown

Why

Since the error-handler module was commented out (c790cb8, 2023), every HTTP failure reaches the fallbacks as jclouds' HttpResponseException (from CloseContentAndSetExceptionErrorHandler). That exception carries the response (getResponse().getStatusCode()) and its body (getContent()), but every fallback parsed only throwable.getMessage():

command: GET https://…/browse/README.md HTTP/1.1 failed with response: HTTP/1.1 404 Not Found; content: [{"errors":[…]}]

That text is not JSON, so even Bitbucket's own errors came back as Failed to parse output: … MalformedJsonException, with the status lost. brain-backend has been guessing it from that text (regexes on statusCode=, "does not exist at revision", "a status-less MalformedJsonException is a 404"), and the guesses misfire in prod: tenant 1782 gets ~11k "Could not find repo" a day for repositories that answer 200 (CD-606 follow-up).

What

  • Error.statusCode() (nullable): the HTTP status of the failed response; null when there was no response (timeout, connection reset). The existing 5-argument Error.create stays, so callers compile unchanged.
  • BitbucketFallbacks.getErrors(Throwable): finds the HttpResponseException in the cause chain, parses its response body (falling back to the message when there is no body, as before), and sets the status on every error. All 51 fallbacks use it; RawContentOnError sets the status too.
  • check now runs mockTest. CI only ran check, which ran no tests, so the published jar was untested; four mock tests were failing on master for exactly this bug (testListBranchesNonExistent, testGetPullRequestNonExistent, testCreatePullRequestEmpty, testMergePullRequestNeedsRetry) and pass now.
  • Version 3.1.4-CORTEX (GitHub Packages does not overwrite a published version).

Not changed: re-binding BitbucketErrorHandler. It is dead code since 2023, and binding it would change which exception types every call throws.

Tests

Mock tests through the real jclouds stack (MockWebServer):

Response Expected
listLines 404 with Bitbucket's JSON error status 404, Bitbucket's message and exception name
listLines 404 with an HTML body status 404
listLines 503 without a body status 503
listLines 200 with an unreadable body status 200
getDefault 404 for a missing repository status 404, "Repository … does not exist."
getDefault relay 555 status 555
raw 404 status 404

Unit: a failure without a response has no status; a wrapped HttpResponseException keeps its status.

./gradlew check (JDK 17): 265 mock tests, 0 failures.

Follow-up

brain-backend: bump to 3.1.4-CORTEX, classify by Error.statusCode() (404 = not found, 555 = relay unavailable) and drop the text heuristics.

🤖 Generated with Claude Code

Since the error-handler module was disabled (c790cb8), every HTTP failure
reaches the fallbacks as jclouds' HttpResponseException, which carries the
response and its body. The fallbacks parsed only the exception's message
("command: GET ... failed with response: HTTP/1.1 404 ...; content: [...]"),
so even Bitbucket's own JSON errors came back as "Failed to parse output ...
MalformedJsonException", and callers had to guess the status from that text.

- Error gains a nullable statusCode: the HTTP status of the failed
  response, or null when there was no response (e.g. a timeout).
- BitbucketFallbacks.getErrors(Throwable) finds the HttpResponseException
  in the cause chain, parses its response body (the message when there is
  none, as before) and sets the status on every error. All fallbacks use
  it; RawContentOnError sets the status too.
- check now runs mockTest, so the publish job tests what it releases. Four
  mock tests that failed on master pass with this change.
- Version 3.1.4-CORTEX.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mmaikel
mmaikel marked this pull request as ready for review October 1, 2026 11:27
@mmaikel
mmaikel merged commit 4ca0778 into master Oct 1, 2026
2 checks passed
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