Skip to content

fix(errors): correct the API error code table and export the helpers - #1867

Open
MartinCupela wants to merge 1 commit into
masterfrom
fix/api-error-codes
Open

MartinCupela wants to merge 1 commit into
masterfrom
fix/api-error-codes

Conversation

@MartinCupela

@MartinCupela MartinCupela commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

CLA

  • Code changes are tested

Description of the changes, What, Why and How?

Supersedes #1864, which exports src/errors from the entry point. That export is included here, but it is not enough on its own: the table it makes public has drifted badly from the codes the API actually sends, so exporting it as-is would turn the drift into public API.

I compared APIErrorCodes against the canonical list the API defines. The table had 27 entries; the API defines 44. Four of the 27 have no server-side counterpart at all.

Codes that were wrong

was is
3 AuthenticationFailedError auth failure is 5
23 RequestTimeoutError request timeout is 48
24 MaxHeaderSizeExceededError no counterpart — removed
69 ErrWrongRegion no counterpart — removed

isErrorRetryable returns false on a table miss, so a real 408 was classified as non-retryable and BaseSearchSource / MessageDeliveryReporter / the WS fallback gave up on timeouts instead of retrying them. The two renumbered entries keep their original retryable flags.

Codes that were missing

19 added, grouped by area: 72, 73, 80, 81, 82, 100106, 107, 108, 109, 110, 111, 112, 113.

Video codes are included even though this client never calls the video endpoints, so the table stays a faithful mirror rather than a partial one. Easy to drop if you'd rather keep it chat-only.

The rule behind retryable

Worth stating, because nothing anywhere defines it and the flags have been hand-maintained since 2021 with no written rationale. HTTP status does not predict it — 60 CoolDown and 17 NotAllowed are both 403 with opposite flags, as are 45 and 44 at 400.

The rule the existing entries imply: can time alone fix it? true when the identical request, resent unchanged, may succeed later with no action from the caller (overload, timeout, capacity, cooldown expiry). false when something has to change first — input, credentials, permissions, app config, billing state.

Applying that rule consistently also flips 82 VideoCreateCallFailed to true: it wraps an upstream video provider failure behind a 400, structurally identical to 45 CustomCommandEndpointCallError, which has always been true.

One entry is genuinely undecidable: 101 is emitted by two different server constructors, one a 500 (transient) and one a 400 (definitive). A client holding only the code cannot tell them apart. I chose true, favouring the 500 flavour, but the real fix is server-side.

A bug this turned up

offline_sync_manager.ts skipped its resetDB() on code === 23, meaning "the sync timed out, so don't wipe the data — a timeout says nothing about how stale the DB is". Since the API never sends 23, that branch never fired, and every timed-out /sync wiped the offline database instead. Now keyed to 48. An existing test was asserting the broken behaviour with a hand-built code-23 error; updated.

Happy to split this into its own PR if you'd rather keep this one to the table.

Testing

One test added, covering the only behaviour here that is not just data: isErrorRetryable falling through to false for an unknown code and for an error with no code. The table's contents are not asserted — a test that restates the table would only duplicate it. The existing offline-sync timeout test was updated to the correct code.

yarn lint, yarn types and yarn build all pass; full suite is 3161 passed / 1 skipped against a 3152 baseline. Verified against the built bundle that the helpers are reachable from the entry point at runtime and that isErrorRetryable({ code: 48 }) is now true.

Follow-ups, not in this PR

  • release-v10 carries the same 27-entry table and needs the same fix. Entry names are unchanged here, so isDoesNotExistError (which matches on the name) ports cleanly.
  • The API returns an unrecoverable flag on error responses, but sets it in only three places server-side. If that were set consistently, isErrorRetryable could prefer the server's own answer and keep this table as the fallback for older backends — which would stop the drift recurring. Worth raising with the backend team.

Changelog

  • Fix APIErrorCodes entries that did not match the codes the API sends, so isErrorRetryable correctly identifies request timeouts and other transient failures as retryable
  • Fix the offline sync manager resetting the offline database after a timed-out sync
  • Export the error helpers (APIErrorCodes, isAPIError, isErrorRetryable, isConnectionIDError, isWSFailure, isErrorResponse) from the package entry point

🤖 Generated with Claude Code

The table in errors.ts had drifted from the codes the API actually sends. Four
entries had no server-side counterpart at all, and 17 codes were missing, so
isErrorRetryable silently fell through to "not retryable" for errors that are
transient.

Renumber the two codes that were simply wrong: auth failure is 5, not 3, and
request timeout is 48, not 23. Their retryable flags carry over unchanged.

Drop 24 (MaxHeaderSizeExceededError) and 69 (ErrWrongRegion), which the API
does not define.

Add the 19 remaining codes, grouped by area. The retryable flag follows the
rule the existing entries imply: true when the identical request may succeed
later with no action from the caller (overload, timeout, capacity), false when
something must change first (input, credentials, permissions, app config).
That rule also makes 82 retryable, matching 45 — both wrap an upstream call
failure behind a 400.

Fix one caller that had been keyed to the wrong code: the offline sync manager
skipped its DB reset on code 23, meaning "the sync timed out, keep the data".
Since the API never sends 23, that branch never fired and every timed-out sync
wiped the offline database instead.

Export the module from the package entry point so consumers can reach the
helpers, which is what #1848 asked for.

Fixes #1848

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@isekovanic isekovanic left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good if these indeed changed and need to be regenerated, but is this on the V2 API only ? Or has it always just been broken ?

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.

3 participants