Skip to content

fix(services): give every NodeDB error a deliberate category - #11

Merged
laksamanakeris merged 1 commit into
mainfrom
fix/error-mapping
Oct 25, 2026
Merged

laksamanakeris merged 1 commit into
mainfrom
fix/error-mapping

Conversation

@laksamanakeris

Copy link
Copy Markdown
Collaborator

Summary

nodedb-types 0.6 added error variants that From<NodeDbError> for StudioError sent to Server through its catch-all arm. Every variant now has a deliberate category and Retry decision, chosen from what the v0.6.2 server means by it.

  • Explicit arms (category_of): each of the 85 ErrorDetails variants has an arm. The required _ arm (foreign #[non_exhaustive] enum) catches only variants added after 0.6.2.
    • Auth: AuthenticationFailed.
    • NotFound: DatabaseNotFound, UndefinedObject, NotFound.
    • Conflict: AlreadyExists, DependentObjectsExist, TransactionRollback, CloneDependency, MoveTenantAlreadyAtTarget.
    • ReadOnly: CloneWriteRequiresMaterialize.
    • Setup: user SQL errors, statement and tenant limits, clone, backup and move-tenant refusals.
    • Connection: RateExceeded, QuotaExceeded (the connection cap), StaleReadNotLeader, Cluster (a version mismatch).
    • Server: Dispatch, MemoryExhausted (moved from Connection), DeadlineExceeded, infrastructure faults.
  • Retry overrides (studio_retries): ServerOverload and RateExceeded are retriable although the client's is_retriable() says no. The server refuses both before anything runs.
  • No Retry for Dispatch: the server also raises it after a write whose outcome is unknown (response channel closed).
  • is_retriable doc: true means transient, not safe to re-send a write. DeadlineExceeded, SyncConnectionFailed, NotLeader and NodeUnreachable are client-retriable and can each follow a committed write.

Tests

  • every_error_code_has_a_deliberate_category_and_retry_flag: walks all 86 codes in ErrorCode::ALL against a table of expected category and retry flag. It fails on a code without a row or without an explicit arm, and lists every mismatch in one run.
  • studio_retries_only_what_the_client_does_not: fails when an upstream fix makes an override redundant, so it can be removed.
  • studio_originated_errors_are_not_retriable: covers the four Studio-originated variants.
  • The table replaces 11 single-variant mapping tests. Test count goes from 276 to 267.

Notes

  • Coverage limit: a future variant that reuses an existing ErrorCode is not caught. No test can list the variants of a foreign #[non_exhaustive] enum. Diff nodedb-types error/details.rs on each crate bump.
  • Write retry: every Retry today re-runs a read or the idempotent mark_all_read, so no current Retry is unsafe. Record-write Retry needs a per-operation policy, not this flag alone.
  • Native login failure: the v0.6.2 server sends it with error code 0, so the client builds Internal and Studio shows "server error". The AuthenticationFailed arm is correct but does not fire for it. Upstream, not fixable here.
  • No UI change: the mock's only error (NodeUnreachable) keeps its category, so screenshots stay the same.

Test plan

  • cargo fmt --all --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo nextest run: 267 passed
  • Mutation checks: a dropped row, a duplicate row, a missing arm, a wrong category, a wrong retry flag, and a redundant override each fail the right test, naming the code
  • CI passes on this PR

nodedb-types 0.6 added error variants that the catch-all arm sent to
Server. Each ErrorDetails variant now has an explicit arm in
category_of, chosen from what the v0.6.2 server means by it:

- Auth: AuthenticationFailed.
- NotFound: DatabaseNotFound, UndefinedObject, NotFound.
- Conflict: AlreadyExists, DependentObjectsExist, TransactionRollback,
  CloneDependency, MoveTenantAlreadyAtTarget.
- ReadOnly: CloneWriteRequiresMaterialize.
- Setup: user SQL errors, statement and tenant limits, and clone,
  backup and move-tenant refusals the request must change.
- Connection: RateExceeded, QuotaExceeded (the connection cap),
  StaleReadNotLeader, Cluster (a version mismatch).
- Server: Dispatch, MemoryExhausted, DeadlineExceeded, and
  infrastructure faults.

ServerOverload and RateExceeded are now retriable, although the client
says no. Both are refused before anything runs. Dispatch is not
retriable: the server also raises it after a write whose outcome is
unknown. The is_retriable doc states that true means transient, not
safe to re-send a write.

A test walks all 86 codes in ErrorCode::ALL against a table of expected
category and retry flag. It fails on a code without an explicit arm, so
a crate bump cannot add a code silently. A second test fails when an
upstream fix makes a retry override redundant. They replace 11
single-variant mapping tests.
@laksamanakeris
laksamanakeris merged commit 1e469b1 into main Oct 25, 2026
6 checks passed
@laksamanakeris
laksamanakeris deleted the fix/error-mapping branch October 25, 2026 05:38
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