Skip to content

Migrate library/import and catalog/provider errors to translatable codes (phases 2+3/4) - #144

Merged
Logan2234 merged 2 commits into
mainfrom
feat/migrate-library-catalog-error-codes
Aug 27, 2026
Merged

Migrate library/import and catalog/provider errors to translatable codes (phases 2+3/4)#144
Logan2234 merged 2 commits into
mainfrom
feat/migrate-library-catalog-error-codes

Conversation

@Logan2234

Copy link
Copy Markdown
Owner

Summary

Phases 2 and 3 of the "Migrate API errors to error codes, domain by domain" Quackback ticket (post_01m119rsxxecw9tbtm8mw37aqa), following its suggested order: library/import (highest-traffic flows, ImportWizard.svelte already tries to render API messages verbatim), then catalog/providers (TMDB/AniList/IGDB/Open Library/MusicBrainz).

  • Migrates 61 bare NestJS exceptions across 22 filesapps/api/src/library/, import/, catalog/, and (since they mirror library's exact entry-ownership shape) games/, books/, music/ too.
  • Codes are shared, not duplicated per domain: LibraryEntryNotFound/LibraryEntryForbidden/LibraryReplayNotFound/LibraryReplayForbidden cover media/games/books/music alike; CatalogItemNotFound/CatalogProviderUnavailable cover every catalogue provider; CatalogSearchQueryRequired covers the 3 identical "Query 'q' is required" controllers.
  • Two shared utils got the fix once, at the source, rippling everywhere they're used:
    • common/http.util.ts's fetchJson() (used by TMDB, AniList, IGDB) — tmdb.provider.ts needed zero changes despite having its own share of BadGateway/NotFound cases, since they all flow through here.
    • common/parse-enum-param.util.ts (catalog/games/books/music/stats/users route params) — one ErrorCode.InvalidParam + params.label/.value.
  • Closes two more hardcoded-French leaks the original ticket's inventory missed: Steam's "profil introuvable"/"bibliothèque inaccessible" messages, and the free-import-quota ForbiddenException.
  • 32 new ErrorCode entries, fr/en translations, resolveApiError() wiring.

A note on message text

AppException's dev-facing message was preserved wherever a test (or genuine debugging value — "which provider failed") depended on the specific text, even when the code is shared across sites. Three specs initially broke this way (open-library, musicbrainz, library.service's season lookup) after I let the message default to the bare code string; fixed by passing the original text back through explicitly.

Test plan

  • pnpm --filter @loomkeep/api exec jest — 821/821
  • pnpm --filter @loomkeep/web vitest — 13/13 (translation completeness now covers 64 codes total)
  • pnpm --filter @loomkeep/web check — 0 errors
  • pnpm lint — 0 errors (104 expected no-restricted-syntax warnings on the remaining phase-4 domains, down from 165)
  • e2e — same Turnstile-in-sandbox limitation as the previous two PRs, not re-verified here

Remaining on the ticket

Phase 4 (social/reviews/comments/reports/lists/admin — the tail), and the Swagger error-code decorator mentioned in "Also in scope".

🤖 Generated with Claude Code

…codes (phases 2+3/4)

Phases 2 and 3 of "Migrate API errors to error codes, domain by domain" —
library/import (highest-traffic flows, ImportWizard's own error surface)
and catalog/providers (TMDB/AniList/IGDB/Open Library/MusicBrainz).

Migrates every remaining bare NestJS exception in apps/api/src/library/,
import/, catalog/, games/, books/ and music/ (61 throw sites across 22
files) to AppException + a dedicated ErrorCode. games/books/music mostly
mirror library/'s entry-ownership shape (*LibraryService,
*.controller.ts "Query 'q' is required"), so their codes are shared rather
than duplicated per domain — same for catalog: CatalogItemNotFound/
CatalogProviderUnavailable cover every provider (AniList, IGDB, Open
Library, MusicBrainz), migrated once at the source in the two shared
utils every provider funnels through:
- common/http.util.ts's fetchJson() — used by TMDB, AniList and IGDB —
  now builds the envelope directly, which is why tmdb.provider.ts needed
  no changes despite having its own quota of BadGateway/NotFound cases.
- common/parse-enum-param.util.ts (catalog/games/books/music/stats/users
  route param parsing) gets one ErrorCode.InvalidParam + params for the
  label/value, rippling to every controller that calls it.

Also closes two more hardcoded-language leaks the original ticket didn't
catch: Steam's French "profil introuvable"/"bibliothèque inaccessible"
messages, and the free-import-quota French ForbiddenException.

32 new ErrorCode entries, fr/en translations, resolveApiError() wiring.
Existing specs updated — message-text assertions (`.rejects.toThrow("...")`)
were left alone since AppException's dev-facing `message` still carries
the same text where a test or genuine debugging need cares about it (e.g.
which provider failed); status/class-based assertions were switched to
`.rejects.toMatchObject({ code })`.

Remaining: phase 4 (social/reviews/comments/reports/lists/admin — the
tail), and the Swagger error-code decorator mentioned in the ticket's
"Also in scope".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@github-actions github-actions Bot added api apps/api (NestJS) web apps/web (SvelteKit) shared packages/shared labels Aug 27, 2026
Covers admin, comments, lists, newsletter, notifications, reports,
reviews, social, stats and users — every bare NestJS exception in
apps/api/src is now an AppException with an ErrorCode, with fr/en
translations wired into apps/web's resolver. The no-restricted-syntax
guardrail flips from warn to error now that the migration is complete.

Also fixes a real regression found along the way: AllExceptionsFilter
was collapsing every 5xx AppException's code to a generic InternalError
before the client ever saw it (the status>=500 branch ran before the
AppException check), silently discarding codes already shipped in
phases 2/3 like CatalogProviderUnavailable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Logan2234
Logan2234 merged commit ef1d7f2 into main Aug 27, 2026
15 of 16 checks passed
@Logan2234
Logan2234 deleted the feat/migrate-library-catalog-error-codes branch August 27, 2026 17:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api apps/api (NestJS) shared packages/shared web apps/web (SvelteKit)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant