Skip to content

Expose a typed error on Result - #210

Merged
edridudi merged 2 commits into
mainfrom
claude/typed-error-model
Sep 12, 2026
Merged

edridudi merged 2 commits into
mainfrom
claude/typed-error-model

Conversation

@edridudi

Copy link
Copy Markdown
Collaborator

Step 2 of #6.

Koios is served by PostgREST, which reports failures as a JSON object:

{"code":"42703","details":null,"hint":null,"message":"column record.no_such_column does not exist"}

Callers only ever saw that as an opaque String on Result.response, and had to parse it themselves to do anything useful — branch on the error, surface a message, or tell a client mistake from a server one.

Change

  • New KoiosError model: code, message, details, hint
  • New Result.error field, populated when a failing response carries a body of that shape

Nothing existing changes

Result.response is untouched and still holds the raw body, so no current caller behaves differently — Result.error is simply null where nothing was available before. That felt like the right trade given 1.22.1 already carries breaking changes; replacing response would have been cleaner but broken every consumer reading it today.

Two deliberate restraints:

  • Bodies that are not JSON of that shape leave error null rather than guessing. A plain-text 500 gets getResponse() == "upstream failure" and getError() == null.
  • Only the two sites where the body genuinely comes from the server parse it. The synthetic failures built locally — the invented 404 for an empty body, 500 for a null one, 400 from badRequestResult — do not fabricate a server error. Separating those properly is step 3 of Support Error Handling for All Relevant Endpoints #6.

Testing

Three new cases in the MockWebServer suite: a full PostgREST body parses to all four fields, a partial body parses what is present and leaves the rest null, and a plain-text body leaves error null while keeping the raw text.

mvn test -Dtest='*Preview*,BaseServiceErrorHandlingTest,ProposalDeserializationTest,*Bech32*'   # 141/141 pass

Also verified against live mainnet by filtering on a column that does not exist:

LIVE raw={"code":"42703","details":null,"hint":null,"message":"column record.no_such_column does not exist"}
LIVE parsed=KoiosError(code=42703, message=column record.no_such_column does not exist, details=null, hint=null)

🤖 Generated with Claude Code

dudiedri and others added 2 commits September 12, 2026 22:04
Step 2 of #6. Koios is served by PostgREST, which reports failures as a
JSON object:

  {"code":"42703","details":null,"hint":null,
   "message":"column record.no_such_column does not exist"}

Callers only ever saw that as an opaque String on Result.response and had
to parse it themselves.

Adds KoiosError (code, message, details, hint) and a Result.error field
populated when a failing response carries a body of that shape. The
existing response String is untouched, so no current caller changes
behaviour - Result.error is simply null where it used to be unavailable.

Bodies that are not JSON of that shape, such as a plain-text 500, leave
error null rather than guessing. Only the two sites where the body
genuinely comes from the server parse it; the synthetic failures built
locally do not pretend to carry a server error.

Verified against live mainnet: a real 42703 parses to
KoiosError(code=42703, message=column record.no_such_column does not
exist, details=null, hint=null).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
KoiosError referenced Result#getError() and Result#getResponse(), which
Lombok generates at compile time and javadoc therefore cannot resolve:

  KoiosError.java:14: error: reference not found

That failed the CodeQL build, which runs the full javadoc. References the
fields instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@edridudi
edridudi merged commit a29d132 into main Sep 12, 2026
3 checks passed
@edridudi
edridudi deleted the claude/typed-error-model branch September 12, 2026 19:20
edridudi added a commit that referenced this pull request Sep 12, 2026
Step 4 of #6, and the last item on it.

Koios enforces a request body size limit on the bulk endpoints. Exceeding
it returns 413 with a plain-text body:

  Payload too large, body length was 18022. Please ensure your request
  body size is below 5120 bytes

Because that is text rather than JSON, the parser added in #210 correctly
leaves Result.error null, so callers got a bare status and had to know on
their own that the fix is to batch.

413 now produces a KoiosError carrying the server's message verbatim plus
a hint to split the request. Nothing is invented about the response
itself: the status is real, so synthetic stays false, and the message is
the server's own words. Only the remedy is ours, because Koios does not
supply one.

Co-authored-by: dudiedri <dudi.edri@ox.security>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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