Skip to content

Split overloaded error codes 104001-104003 - #288

Open
lmars wants to merge 2 commits into
mainfrom
new-error-codes
Open

Split overloaded error codes 104001-104003#288
lmars wants to merge 2 commits into
mainfrom
new-error-codes

Conversation

@lmars

@lmars lmars commented Jul 31, 2026

Copy link
Copy Markdown
Member

AIT-1259

Three of the SDK's error codes each covered several unrelated failures, so a developer could not switch on the code and react appropriately. Writing the registry entries for ably-common#353 exposed this — each entry had to say "this error takes two forms, with different fixes", which is a smell that the code itself is wrong.

The splits

Was Failure Now
104001 Channel subscribe/attach failed during connect() — nothing sends or receives 104001 SessionSubscriptionError
104001 Processing one inbound message threw — the session keeps working 104009 SessionMessageProcessingFailed
104002 onCancel threw — the SDK never reaches the abort, so the run is not cancelled 104002 RunCancelHandlerFailed
104002 onSteer threw — the steering message already folded in, so only the notification failed 104012 RunSteerHandlerFailed
104002 Cancel-message routing failed — not a developer-handler fault at all 104013 RunCancelRoutingFailed
104003 A run-lifecycle publish failed 104003 RunLifecycleEventPublishFailed

Each now describes one failure with one recovery, and the recovery differs in each case: retry connect() versus fix your handler; the run is dead versus the run is fine.

104003 now covers steps

publishLifecycle() wrapped the run-lifecycle publishes only. The step-lifecycle publishes — runManager.startStep() and endStep() — let the raw Ably publish error propagate, though a failed ai-step-start is the same class of failure as a failed ai-run-start.

That bracket moves to src/core/transport/lifecycle-publish.ts and both tiers now use it, so neither can drift on the error code, message shape, or cause preservation. The code is renamed to reflect the widened scope.

Naming

Members are named after their registry identifier, which keeps a run_ or session_ prefix so the AIT codes group together. These are the names that generating the constants from the registry (ably-common#357) will derive, so adopting them now avoids renaming the same members twice. The members this PR does not touch still differ from their identifiers; that migration is what settles them.

ErrorCode.CancelListenerError is removed and RunLifecycleError renamed, so this is a breaking change to the exported ErrorCode enum.

ably-common

Pinned at the merge of #353. protocol/errors.json no longer maps a code straight to a description string — entries sit under a codes envelope, each an object with an identifier, title and summary. validate-error-codes.ts indexed the top level, so against the bumped pin it would have found nothing and reported all 19 codes as missing. It now reads codes, and reports an absent envelope as a stale or uninitialised submodule rather than letting it read as 19 unregistered codes.

Testing

check:error-codes resolves all 19 codes. New unit coverage for each newly distinguished failure: the onSteer throw, the cancel-routing failure, and a failed ai-step-start / ai-step-end publish. The routing path is reachable when a run's own onError throws while reporting an onCancel failure — the second throw escapes the cancel handler and lands in the routing bracket — and the test drives it that way. publishLifecycleEvent gets its own test file.

Full suite green: 69 files, 1677 tests.

🤖 Generated with Claude Code

lmars and others added 2 commits July 31, 2026 16:32
Three of the SDK's error codes each covered several unrelated failures, so
a developer could not switch on the code and react appropriately. Writing
the registry entries for ably-common exposed this: each had to say "this
error takes two forms, with different fixes".

104001 covered both a connect-time channel attach failure, after which
nothing sends or receives, and a throw while processing one inbound
message, after which the session keeps working. The second becomes 104009,
so the code tells a caller whether the session is usable.

104002 covered three sites: onCancel throwing, which leaves the run NOT
cancelled because the SDK never reaches the abort; onSteer throwing, where
the steering message has already folded in so only the notification
failed; and a cancel-dispatch failure, which is not a developer-handler
fault at all. They become 104002, 104012 and 104013 respectively.

104003 wrapped run-lifecycle publishes only - the step-lifecycle publishes
let the raw Ably publish error propagate, though a failed ai-step-start is
the same class of failure as a failed ai-run-start. Its bracket moves to
lifecycle-publish.ts and both tiers now use it, so neither can drift on
the code, message shape, or cause preservation. The code is renamed to
match its widened scope.

The cancel-dispatch path is reachable when a run's own onError throws
while reporting an onCancel failure: the second throw escapes the cancel
handler and lands in the routing bracket. The new test drives it that way.

check:error-codes fails until 104012 and 104013 are registered in
ably-common and the submodule is bumped. 104009 is registered there for a
failure nothing raises, so that entry is replaced rather than added.

[AIT-1259]

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The AIT error codes are now registered, so pin ably-common at the merge of
ably/ably-common#353. That brings in 104009, 104012 and 104013, which the
previous commit's enum referred to before they existed, and renames
104003's identifier to run_lifecycle_event_publish_failed.

protocol/errors.json no longer maps a code straight to a description
string. Entries now sit under a "codes" envelope, each an object with an
identifier, title and summary. validate-error-codes.ts indexed the top
level, so against the bumped pin it would have found nothing and reported
all 19 codes as missing - a misleading failure rather than a clean pass.
It now reads codes and prints each entry's identifier, and reports an
absent envelope as a stale or uninitialised submodule instead of letting
it read as 19 unregistered codes.

The check compares codes, not names. Generating the constants from the
registry (ably/ably-common#357) is what makes the registry identifier the
one spelling every SDK uses, and replaces this script.

[AIT-1259]

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant