Skip to content

fix(api): let 5xx errors through the tool routes' 422 catch-all - #1667

Merged
snapotter-hq merged 1 commit into
snapotter-hq:mainfrom
drakeo338:claude/tool-routes-5xx
Sep 30, 2026
Merged

snapotter-hq merged 1 commit into
snapotter-hq:mainfrom
drakeo338:claude/tool-routes-5xx

Conversation

@drakeo338

Copy link
Copy Markdown
Contributor

What does this PR do?

Refs #1640. The hand-written tool routes answered 422 for any error in their catch-all, so a full workspace or failed write read as a bad file. They now rethrow errors carrying a 5xx statusCode, like image-to-pdf does for isDecoderUnavailable, via a small shared hasServerErrorStatus predicate. Does not close #1640 (the factory route).

Routes: svg-to-raster (and its presets), image-to-pdf, beautify, compare, compose, content-aware-resize, edit-metadata, meme-generator, stitch, vectorize, watermark-image, passport-photo (analyze), remove-background.

Skipped: pdf-to-image (overlaps #1552).

Tests: new unit test drives svg-to-raster and compare with putObject throwing 507/503 (now surfaced) and a plain error (still 422).

Type of change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • New translation or i18n update
  • Documentation update
  • Test improvement
  • Refactor (no functional change)

Checklist

  • I have read CONTRIBUTING.md
  • I have signed the CLA (the bot will prompt you if not)
  • My changes follow the project's code style (Biome passes)
  • I have added or updated tests for my changes
  • All existing tests pass locally (pnpm test)
  • TypeScript compiles without errors (pnpm typecheck)
  • My PR is focused on a single concern (under 400 lines of change)
  • I have not modified CI, release, or linter configuration files

Screenshots (if applicable)

Not applicable.

🤖 Generated with Claude Code

Refs snapotter-hq#1640. A full workspace or failed storage write inside these routes was answered as a 422 bad file. Rethrow errors carrying a 5xx statusCode, as image-to-pdf already does for isDecoderUnavailable.
@snapotter-hq
snapotter-hq merged commit 3512cb2 into snapotter-hq:main Sep 30, 2026
21 checks passed
@snapotter-hq

Copy link
Copy Markdown
Owner

Review notes before merge. Nothing blocking turned up. The new tests fail on the base commit, and the global error handler logs, reports, and answers the rethrown 503/507 with their message and code, so skipping the per-route request.log.error in four routes doesn't lose anything. Scratch cleanup still runs in the inner finally blocks.

Filed as #1671: five catches with the same shape that this PR doesn't touch. They're the passport-photo.ts generate catch (:566), qr-generate.ts, html-to-image.ts, the content-aware-resize.ts prepare catch, and sign-pdf.ts. The issue also sketches a static drift test so the next route can't reintroduce it.

Left to #1640: bridge errors with kind: "operational" and no statusCode (OOM kill, timeout) still come back as 422 from content-aware-resize and passport-photo. Whether those should map to 503 is the policy question #1640 already asks.

Not actioned:

  • The 503/507 tests assert the status only, not the body's code. The error handler's SafeError branch has its own tests, so this is thin rather than missing.
  • app.close() runs after expect, so a failing test leaves a Fastify instance open. It's inject-only (no socket), and the fork exits anyway.
  • No boundary cases (499/500/599/600) in the predicate test. The >= 500 && <= 599 check is simple enough to read.

@snapotter-hq

Copy link
Copy Markdown
Owner

Thanks @drakeo338. Storage failures in these 13 routes now come back as a 503 or 507 with a real message, not a 422 that blames the user's file. As a side effect, five of them now report a missing decoder correctly too. Merged as submitted, and it ships in the next release. The routes this didn't cover are tracked in #1671 if you want to pick them up.

snapotter-hq added a commit that referenced this pull request Sep 30, 2026
qr-generate, html-to-image, passport-photo generate and the content-aware-resize input-prep catch now rethrow errors carrying a 5xx statusCode instead of answering 422, matching #1667. In html-to-image the rethrow runs before the timeout/browser message checks. sign-pdf is left to #1640 because a waitForJob rejection carries no status.

Closes #1671
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.

Sync tool path answers 422 "Processing failed" for server-side failures too

2 participants