Skip to content

errors: Add bodies for all error codes with an FAQ page#352

Merged
lmars merged 19 commits into
mainfrom
error-docs
Jul 25, 2026
Merged

errors: Add bodies for all error codes with an FAQ page#352
lmars merged 19 commits into
mainfrom
error-docs

Conversation

@lmars

@lmars lmars commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

Writes detail-page Markdown bodies for every error code that had an FAQ page referenced in protocol/errorsHelp.json but no body yet — 79 codes in total. The goal is to make the new website error pages (ably.com/docs/platform/errors/codes) self-sufficient so the old faqs.ably.com links can eventually be retired.

Each body follows the three-section format in errors/guidelines.md (What you should doWhy it happensWhat you'll see) and was verified against source, not the FAQ text alone: the realtime server, the node core, ably-js, @ably/spaces, and the bundled canonical messages. Message strings and HTTP statuses in What you'll see come from the raising code.

What's included

Bodies were added in batches by domain:

  • Generic/HTTP status (10000, 20000, 40000, 40300, 40400, 50000)
  • Request & message validation (40001, 40003, 40006, 40010, 40012, 40013, 40015, 40016)
  • Publish / pagination / batch / misc (40011, 40017, 40020, 40022, 40030, 40031, 40032)
  • Authentication & credentials (40005, 40100–40106, 40144, 40161, 40170, 40171)
  • Token/key revocation (40131, 40133, 40141)
  • Account & application limits (40111, 40112, 40114, 40115, 40125, 40127)
  • Placement constraints (40330, 40331, 40332)
  • TLS (40311)
  • Connection lifecycle (80000, 80002, 80014, 80016, 80017, 80019, 80022)
  • Creation rate limits (80021, 90021)
  • Channels (90001, 90007, 90010)
  • Presence (91000, 91001, 91003, 91005)
  • Internal errors (50001, 50002, 50003)
  • LiveSync connectors (72000, 72003–72007)
  • LiveObjects (92000–92008)
  • Annotations (93002)
  • Spaces / locks (101000–101004)
  • Subscription (71302)

Guidelines & terminology changes

  • Guidelines: What you should do now opens with the action rather than recapping the summary; added a "No em-dashes" rule across all fields.
  • Dictionary: removed "cluster" from the Region row's Don't use list — a dedicated cluster is a distinct, valid customer-facing concept, not a synonym for a geographic region (used by the 40331 placement-constraint entry).

Corrections found while verifying against source

  • 40131 summary: dropped the unsupported "permanently invalidated" claim (not in the FAQ, docs, or code).
  • 92001 summary: corrected "maximum number of objects" to the size-based limit the code actually enforces (channel total object size / per-object max message size); protocol/errors.json regenerated.
  • 40022 and 71302 FAQ pages are stale/gone; bodies follow the registry meaning and the raising code.

Housekeeping

  • Normalized 12 pre-existing bodies off the redirecting /docs/general/limits URL to the canonical /docs/platform/pricing/limits.
  • Swept em-dashes out of 26 pre-existing entries (incl. the 40142.md worked example) to satisfy the new guideline; wording unchanged, punctuation only.

All changes pass npm run validate:errors, and protocol/errors.json is regenerated wherever a summary changed.

🤖 Generated with Claude Code

lmars and others added 18 commits July 23, 2026 17:42
Write detail-page bodies for the three revocation-related error codes that
had an FAQ page but no Markdown body, verified against the realtime auth
code and the token revocation docs:

- 40131 (API key revoked): switch to a valid credential; investigate if
  unexpected. Drop the unsupported "permanently invalidated" claim from the
  summary, keeping it faithful to the FAQ.
- 40133 (wrong API key for token revocation): revoke using the issuing key.
- 40141 (token revoked): split into expected vs unexpected cases; note that
  revocation only invalidates already-issued tokens, so the auth server must
  also stop re-issuing them.

Also sharpen the guidelines: "What you should do" should open on the action,
not recap the summary; and add a "No em-dashes" rule across all fields.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the three placement-constraint error codes that
had an FAQ page but no Markdown body, verified against
common/lib/types/placementconstraints.ts (all HTTP 403):

- 40330 (unspecified): read the accompanying message; configure the custom
  endpoint.
- 40331 (incompatible environment / dedicated cluster): route clients to the
  account's dedicated endpoint via the endpoint client option.
- 40332 (incompatible site / region): route clients to a permitted region.

Use the canonical .ably.net endpoint hostnames (main.realtime.ably.net,
acme.realtime.ably.net) rather than the legacy .ably.io form the code emits,
and the current `endpoint` client option rather than the older `environment`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove "cluster" from the Region row's "Don't use" list in the dictionary.
Cluster and region are not confusable concepts: a dedicated cluster is an
isolated deployment, not a geographic region, and "cluster" is valid
customer-facing terminology for it (as used in the 40331 placement-constraint
error). Keep "site" and "datacenter" as the internal synonyms to avoid in
favour of "region".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the eight request- and message-validation error
codes that had an FAQ page but no Markdown body, verified against the realtime
server, node core, and ably-js (all HTTP 400):

- 40001 invalid request body
- 40003 invalid request parameter value
- 40006 message contains invalid connection ID
- 40010 invalid channel name
- 40012 invalid client ID
- 40013 invalid message data or encoding
- 40015 invalid device ID
- 40016 invalid message name

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the seven request-handling error codes that had
an FAQ page but no Markdown body, verified against the realtime server and
node core (all HTTP 400):

- 40011 pagination sequence no longer valid (restart the enumeration)
- 40017 unsupported protocol version
- 40020 batch request error (inspect per-item errors)
- 40022 API Streamer no longer offered (decommissioned)
- 40030 invalid publish request (unspecified umbrella)
- 40031 invalid client-specified message id
- 40032 invalid message extras field

Note: the 40022 FAQ page is stale (it describes an unrelated Control API
error); the registry meaning verified from the raising code is authoritative.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the twelve auth/credential error codes that had
an FAQ page but no Markdown body, verified against the realtime server, node
core, and ably-js:

- 40005 invalid API key or token (400, unparseable credential)
- 40100 unauthorized
- 40101 authentication failed
- 40102 incompatible credentials
- 40103 basic auth over non-TLS transport
- 40104 token request timestamp outside window
- 40105 nonce value replayed
- 40106 no valid authentication method provided
- 40144 invalid JWT token
- 40161 operation requires an identified client
- 40170 token callback failed
- 40171 token renewal not configured (403)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the six limit error codes that had an FAQ page
but no Markdown body:

- 40111 account connection limit exceeded (403)
- 40112 account message limit exceeded (401)
- 40114 account channel limit exceeded (403)
- 40115 account request limit exceeded (403)
- 40125 application integration limit exceeded (401)
- 40127 application API key limit exceeded (401)

Focus remediation on restoring service: for the package-tied limits
(connections, messages, channels, requests) upgrade the package or request a
higher limit from support, with the incident escalation process called out for
Enterprise customers; the integration-rule and API-key limits are fixed across
packages, so they route to support only. Link the canonical pricing/limits and
support pages. Statuses and messages verified against account.ts and the
node core where raised; the account-wide codes are data-driven so status is
taken from the block/restriction defaults.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the seven connection-lifecycle error codes that
had an FAQ page but no Markdown body, verified against ably-js
connectionerrors.ts, the node core, and the realtime comet transport:

- 80000 connection failed (terminal; call connect() after fixing the cause)
- 80002 connection suspended (self-healing; history to catch up)
- 80014 connection timed out (auto-retried)
- 80016 connection replaced by a newer one (non-fatal, 410)
- 80017 connection closed (deliberate, 400)
- 80019 token request failed (authUrl/authCallback, 401/403)
- 80022 connection not found (non-fatal routing, 410)

Keep the prose in customer terms (connection, not transport), and point
suspended-state readers at the connection state change reason and the history
API rather than vague network advice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the two account-wide creation-rate error codes
that had an FAQ page but no Markdown body, verified against account.ts:

- 80021 account-wide connection creation rate exceeded (fatal, 429)
- 90021 account-wide channel creation rate exceeded (non-fatal, 429)

Follow the existing instantaneous rate-limit family's style (42917/42918).
The fatal/non-fatal split and message form are taken from the raising code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the nine LiveObjects error codes that had an FAQ
page but no Markdown body, verified against the realtime server
(lib/errors/helpers.go) and the ably-js liveobjects plugin:

- 92000 invalid LiveObjects message (400)
- 92001 LiveObjects limit exceeded (400)
- 92002 operation on a deleted object (400)
- 92003 root has been deleted (400)
- 92004 object not found (404)
- 92005 path matched no objects (400)
- 92006 operation missing object reference (400)
- 92007 operation not valid for the path (400)
- 92008 sync did not complete (400)

Correct the 92001 summary: the raising code enforces a size limit (channel
total object size, and per-object max message size), not a maximum number of
objects as the old summary and FAQ stated. Its remediation distinguishes the
package-tied max message size (upgrade) from the fixed 6.5 MB per-channel total
(support). Regenerate protocol/errors.json for the 92001 summary change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the three channel error codes that had an FAQ
page but no Markdown body, verified against ably-js realtimechannel.ts and the
realtime server:

- 90001 channel in an invalid state for the operation (SDK 400 / server 404)
- 90007 channel operation timed out (408; attach/detach, self-healing)
- 90010 too many channels (400; per-connection and per-batch-request limits)

For 90001, give state-specific recovery (inspect the channel error reason;
attach() to recover a failed channel; suspended re-attaches automatically;
detach before release) rather than the FAQ's non-idiomatic "operate inside
state-change callbacks" advice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the four presence error codes that had an FAQ
page but no Markdown body, verified against ably-js realtimepresence.ts, the
node core, and the SDKs (all HTTP 400):

- 91000 cannot enter presence without a clientId
- 91001 cannot enter presence in the channel's current state
- 91003 too many presence members (per-channel, fixed across packages)
- 91005 presence state out of sync (get during suspended)

91003 routes to support rather than a package upgrade, since the per-channel
presence member limit is the same on every package.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the six LiveSync (Ingress) error codes that had an
FAQ page but no Markdown body, verified against the realtime server
(lib/errors/helpers.go):

- 72000 connector operation failed (500, unexpected internal error)
- 72003 cannot reach the database (500)
- 72004 could not identify target channel (400, missing _ablyChannel)
- 72005 MongoDB pipeline is invalid (400)
- 72006 failed to resume (500, unreadable resume token)
- 72007 failed to track its position (500, ably collection permissions)

Use the customer-facing "LiveSync connector" terminology throughout rather than
the internal "Ingress"/"rule worker" names, while quoting the raw message
strings verbatim in What you'll see.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Write detail-page bodies for the five Spaces error codes that had an FAQ page
but no Markdown body, verified against the @ably/spaces SDK (src/Errors.ts,
all HTTP 400):

- 101000 space name is empty
- 101001 space must be entered first
- 101002 lock request already pending
- 101003 lock already held
- 101004 lock invalidated by concurrent request

The lock errors frame contention as expected behaviour and point at the Spaces
locking documentation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ption errors

Write detail-page bodies for the remaining twelve error codes that had an FAQ
page but no Markdown body, verified against the realtime server, node core,
ably-js, and the bundled canonical messages:

- 10000 no error / 20000 general error (placeholders/catch-all)
- 40000 bad request (400) / 40300 forbidden (403) / 40400 not found (404)
- 50000 internal server error (500)
- 40311 application requires a TLS connection (403)
- 50001 internal channel error (500) / 50002 internal connection error (500)
- 50003 request timed out server-side (503, some paths 500)
- 93002 message annotations/updates/appends/deletes not enabled (400)
- 71302 no subscription to product

40300 links the capability docs; 93002 uses the annotations and
updates-deletes docs' own terminology rather than "mutable messages". 71302's
FAQ page is gone and its raising code is not in the cloned repos, so its body
follows the registry summary and canonical message without asserting a status.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update the twelve pre-existing rate/size-limit bodies that linked
https://ably.com/docs/general/limits (which 301-redirects) to the canonical
https://ably.com/docs/platform/pricing/limits, matching the URL used by the
bodies written in this pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rewrite the em-dash asides in 26 existing entries (including the 40142 worked
example) into plain sentences, per the "No em-dashes" guideline: commas for
brief asides, colons or sentence splits for elaborations, and parentheses for
appositives such as the push-provider lists. Wording and meaning are unchanged;
only punctuation is affected. Quoted message strings are left verbatim.
Regenerate protocol/errors.json for the summaries that changed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each body-bearing entry ended with a blank line before EOF, which
markdownlint flags as MD012 (no-multiple-blanks) in the documentation
format check. Normalize every codes/*.md to end with a single newline.
Whitespace only; no content changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@m-hulbert m-hulbert 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.

Mostly looks good to me, thanks @lmars !

Just a few relatively minor suggested fixes and changes:

  • Since it's only docs consuming these, can we make the links relative /docs/channels/...
  • Do we want to advertise incident escalation for enterprise packages on the rate limits? Or should we at least qualify it with something around out of hours or urgency?
  • I think the support links should go to support rather than ading another hop via the docs page.
  • We've still got a few instances of 'channel rule' creeping in.
  • I've updated dashboard links to link to the relevant dashboard page - might be worth updating the guidelines?

Comment thread errors/codes/20000.md Outdated

## What you should do

Rely on the accompanying message and HTTP status for the specifics, since 20000 itself only signals that the condition didn't map to a more specific code. Capture the message and status, and if the cause isn't clear, [contact Ably support](https://ably.com/docs/platform/support) with them.

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.

Suggested change
Rely on the accompanying message and HTTP status for the specifics, since 20000 itself only signals that the condition didn't map to a more specific code. Capture the message and status, and if the cause isn't clear, [contact Ably support](https://ably.com/docs/platform/support) with them.
Rely on the accompanying message and HTTP status for the specifics, since 20000 itself only signals that the condition didn't map to a more specific code. Capture the message and status, and if the cause isn't clear, [contact Ably support](https://ably.com/support) with them.

Comment thread errors/codes/40005.md Outdated

## What you should do

Check the API key or token the client was configured with. The value couldn't be read at all, so the fix is to supply a correctly-formed credential, not to adjust its permissions. Copy the key from the [Ably dashboard](https://ably.com/dashboard) again in case it was truncated or picked up stray whitespace.

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.

Suggested change
Check the API key or token the client was configured with. The value couldn't be read at all, so the fix is to supply a correctly-formed credential, not to adjust its permissions. Copy the key from the [Ably dashboard](https://ably.com/dashboard) again in case it was truncated or picked up stray whitespace.
Check the API key or token the client was configured with. The value couldn't be read at all, so the fix is to supply a correctly-formed credential, not to adjust its permissions. Copy the key from the [Ably dashboard](https://ably.com/accounts/any/apps/any/app_keys) again in case it was truncated or picked up stray whitespace.

Comment thread errors/codes/40022.md Outdated

## What you should do

API Streamer has been shut down, so requests that depend on it can't be served and won't recover. If your application still targets it, migrate to a current Ably integration. Contact Ably support if you're unsure what replaces your use case.

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.

Suggested change
API Streamer has been shut down, so requests that depend on it can't be served and won't recover. If your application still targets it, migrate to a current Ably integration. Contact Ably support if you're unsure what replaces your use case.
API Streamer has been shut down, so requests that depend on it can't be served and won't recover. If your application still targets it, migrate to a current Ably integration. Contact [Ably support](https://ably.com/support) if you're unsure what replaces your use case.

Comment thread errors/codes/40111.md Outdated

## What you should do

Restore service by upgrading the account to a package with a higher [connection limit](https://ably.com/docs/platform/pricing/limits), or, if you need more than the packages provide, by [contacting Ably support](https://ably.com/docs/platform/support) to request a higher limit. Enterprise customers whose service is disrupted can use the [incident escalation process](https://ably.com/docs/platform/support#escalation) to page Ably's engineering team at any time.

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.

Suggested change
Restore service by upgrading the account to a package with a higher [connection limit](https://ably.com/docs/platform/pricing/limits), or, if you need more than the packages provide, by [contacting Ably support](https://ably.com/docs/platform/support) to request a higher limit. Enterprise customers whose service is disrupted can use the [incident escalation process](https://ably.com/docs/platform/support#escalation) to page Ably's engineering team at any time.
Restore service by upgrading the account to a package with a higher [connection limit](https://ably.com/docs/platform/pricing/limits), or, if you need more than the packages provide, by [contacting Ably support](https://ably.com/support) to request a higher limit. Enterprise customers whose service is disrupted can use the [incident escalation process](https://ably.com/docs/platform/support#escalation) to page Ably's engineering team at any time.

Comment thread errors/codes/40131.md Outdated

## What you should do

Move the client onto a different, valid credential. If you administer the account, select another active API key in the [Ably dashboard](https://ably.com/dashboard) or create a new one; if the client uses [token authentication](https://ably.com/docs/auth/token), issue its tokens from that key instead. If you don't administer the account, ask an admin to do this and to provide the key or a token request generated from it.

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.

Suggested change
Move the client onto a different, valid credential. If you administer the account, select another active API key in the [Ably dashboard](https://ably.com/dashboard) or create a new one; if the client uses [token authentication](https://ably.com/docs/auth/token), issue its tokens from that key instead. If you don't administer the account, ask an admin to do this and to provide the key or a token request generated from it.
Move the client onto a different, valid credential. If you administer the account, select another active API key in the [Ably dashboard](https://ably.com/accounts/any/apps/any/app_keys) or create a new one; if the client uses [token authentication](https://ably.com/docs/auth/token), issue its tokens from that key instead. If you don't administer the account, ask an admin to do this and to provide the key or a token request generated from it.

Comment thread errors/codes/42913.md
If the message matters, republish it after a short delay, increasing the delay if it is rejected again.

To keep within the limit, consider [spreading publishing across more channels](https://faqs.ably.com/how-do-i-avoid-hitting-the-max-channel-message-rate-limit), so traffic is divided between them rather than concentrated on one. If a channel genuinely needs a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits).
To keep within the limit, consider [spreading publishing across more channels](https://faqs.ably.com/how-do-i-avoid-hitting-the-max-channel-message-rate-limit), so traffic is divided between them rather than concentrated on one. If a channel genuinely needs a higher rate, you can [request a higher limit](https://ably.com/docs/platform/pricing/limits).

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.

Would prefer to not link to an FAQ since we're switching them off.

…ology

Apply Mark's review feedback on the error-code bodies:

- Point "contact Ably support" links at https://ably.com/support instead
  of hopping through the docs/platform/support page (16 files). Keep the
  #escalation anchor on docs, where the escalation process is documented.
- Link dashboard actions to the specific page (the app's API keys page)
  rather than the dashboard root (40005, 40131).
- Replace the compound "channel rule" with namespace-centric wording in
  the flagged bodies (40161, 93002), matching Ably's docs which lead with
  "namespace" and reserve "rule" for an individual named setting.
- Qualify the incident-escalation note on account-limit errors so it
  applies to production-disrupting, urgent cases rather than "at any
  time" (40111, 40112, 40114, 40115).
- Codify all of the above in guidelines.md and CLAUDE.md: link dashboard
  actions to the specific page, send support links to the support page,
  and prefer "namespace"/"namespace settings" over "channel rule".

No frontmatter changed, so protocol/errors.json needs no regeneration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lmars

lmars commented Jul 25, 2026

Copy link
Copy Markdown
Member Author

@m-hulbert I've addressed most of your feedback in e06de73, except the following:

Since it's only docs consuming these, can we make the links relative /docs/channels/...

I don't think it's only docs consuming these, we haven't decided yet but we might want to include the "What you should do" sections as hints in errors over the wire, so I've kept them absolute for now.

Do we want to advertise incident escalation for enterprise packages on the rate limits? Or should we at least qualify it with something around out of hours or urgency?

I think so yes, so I've qualified it.

Would prefer to not link to an FAQ since we're switching them off.

I asked Claude to search for equivalent guidance in the docs and I couldn't find it, so I've left the FAQ link because I think it's better to link to something, and then we can replace in the future when we migrate the pages to the docs.

@lmars
lmars merged commit 3bcbe0a into main Jul 25, 2026
2 checks passed
@lmars
lmars deleted the error-docs branch July 25, 2026 12:09
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.

2 participants