Skip to content

fix(next)!: stitchErrorResponse returns Response | undefined so it composes#475

Open
rejifald wants to merge 1 commit into
mainfrom
fix/next-stitcherrorresponse-optional
Open

fix(next)!: stitchErrorResponse returns Response | undefined so it composes#475
rejifald wants to merge 1 commit into
mainfrom
fix/next-stitcherrorresponse-optional

Conversation

@rejifald

Copy link
Copy Markdown
Owner

What

@stitchapi/next's stitchErrorResponse(err) mapped any thrown value to a Response
(a StitchError502, everything else → 500). That made "I handled it" and "not my
error" indistinguishable at the call site: to rethrow a non-stitch error you had to
pre-guard with isStitchError, and the internal 500 branch silently swallowed
unrelated bugs
into an HTTP response.

It now returns undefined for a non-StitchError, so map-or-rethrow collapses to one
branch:

} catch (err) {
    const mapped = stitchErrorResponse(err);
    if (mapped) return mapped; // a Stitch failure → safe JSON Response
    throw err;                 // anything else falls through untouched
}

This is the contract @stitchapi/elysia's stitchErrorResponse already ships on main
(stitchErrorResponse(err) ?? new Response(...)); this aligns @stitchapi/next with it.

⚠️ Breaking

stitchErrorResponse(nonStitchError) was a 500 Response; it is now undefined. Pre-GA,
no deprecation. Callers that relied on the catch-all 500 should pass an explicit fallback
(stitchErrorResponse(err) ?? Response.json(…, { status: 500 })).

Changed

  • src/index.ts — guard if (!isStitchError(err)) return undefined;, return type
    Response | undefined, drop the now-dead non-stitch 500 branch.
  • test/next.spec.ts — flip the non-stitch case to toBeUndefined(); narrow the
    StitchError cases through a small mustRespond helper.
  • README.md + integrations/next.mdx — composition pattern; drop the now-redundant
    isStitchError pre-guard from the snippet.

Context

Carved out of the #470 contract-freeze sweep so this contract change lands and is reviewed
on its own. (In #470 it also picks up an ErrorResponseOptionsStitchErrorOptions rename
and a BAD_GATEWAY const — omitted here to keep the diff to the behavior change.)

Verification

pnpm --filter @stitchapi/next test (18 pass) + check:types clean; full pre-push suite
green (incl. build-docs twoslash-compiling the updated next.mdx).

🤖 Generated with Claude Code

…composes

`stitchErrorResponse(err)` mapped *any* thrown value to a `Response` (a `StitchError` to
502, everything else to 500), so a caller couldn't tell "handled" from "not mine": the
only way to rethrow a non-stitch error was to pre-guard with `isStitchError`, and the
internal 500 branch silently swallowed unrelated bugs into an HTTP response.

It now returns `undefined` for a non-`StitchError`, so the whole map-or-rethrow collapses
to one branch:

    const mapped = stitchErrorResponse(err);
    if (mapped) return mapped;
    throw err;

This is the contract `@stitchapi/elysia`'s `stitchErrorResponse` already ships on `main`
(`stitchErrorResponse(err) ?? new Response(...)`) — this aligns `@stitchapi/next` with it.
Updates the next tests, README, and integration doc to the composition pattern (the now
built-in check makes the doc snippets' `isStitchError` pre-guard redundant).

BREAKING: `stitchErrorResponse(nonStitchError)` was a `500` `Response`; it is now
`undefined`. Pre-GA. Carved out of the #470 contract-freeze sweep so this contract change
lands and is reviewed on its own.

Co-Authored-By: Claude Opus 4.8 <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