From b9ef750e5a7864ed28c06513b638549c2875cd8d Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 03:06:01 +0100 Subject: [PATCH 1/9] Add error codes 40165, 40166 and 103008 to errors.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 40165, 40166: previously curated but missing from the code → title map. - 103008 (Push transport credentials invalid): reserved for the push transport credential-rejection case. Co-Authored-By: Claude Opus 4.8 (1M context) --- protocol/errors.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/protocol/errors.json b/protocol/errors.json index 2933eda6..0ae74961 100644 --- a/protocol/errors.json +++ b/protocol/errors.json @@ -72,6 +72,8 @@ "40162": "operation not permitted with a token, requires basic auth", "40163": "operation not permitted, key not marked as permitting revocable tokens", "40164": "channel state disabled", + "40165": "channel mode not requested when attaching", + "40166": "unidentified client cannot modify own messages", "40170": "error from client token callback", "40171": "no means provided to renew auth token", "40172": "operation only permitted with token auth", @@ -273,5 +275,6 @@ "103004": "Push notification rejected by provider", "103005": "Push device token invalid", "103006": "Push transport not configured", - "103007": "Push notification provider unreachable" + "103007": "Push notification provider unreachable", + "103008": "Push transport credentials invalid" } From be5fa292624503cce257267391e7741e2d054f8a Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 03:08:15 +0100 Subject: [PATCH 2/9] Add error code documentation registry Establish errors/codes/ as the source of truth for which error codes are valid and what they mean: one Markdown file per code, with YAML frontmatter (code, title, summary) and an optional Markdown body for the detail page. Every code has a title and summary; many also carry a body. README, CLAUDE and guidelines describe the registry and how to write entries. Co-Authored-By: Claude Opus 4.8 (1M context) --- errors/CLAUDE.md | 27 ++++++++ errors/README.md | 32 ++++++++++ errors/codes/10000.md | 5 ++ errors/codes/101000.md | 5 ++ errors/codes/101001.md | 5 ++ errors/codes/101002.md | 5 ++ errors/codes/101003.md | 5 ++ errors/codes/101004.md | 5 ++ errors/codes/102000.md | 5 ++ errors/codes/102001.md | 5 ++ errors/codes/102002.md | 5 ++ errors/codes/102003.md | 5 ++ errors/codes/102004.md | 5 ++ errors/codes/102005.md | 5 ++ errors/codes/102050.md | 5 ++ errors/codes/102051.md | 5 ++ errors/codes/102052.md | 5 ++ errors/codes/102053.md | 5 ++ errors/codes/102054.md | 5 ++ errors/codes/102100.md | 5 ++ errors/codes/102101.md | 5 ++ errors/codes/102102.md | 5 ++ errors/codes/102103.md | 5 ++ errors/codes/102104.md | 5 ++ errors/codes/102105.md | 5 ++ errors/codes/102106.md | 5 ++ errors/codes/102107.md | 5 ++ errors/codes/102108.md | 5 ++ errors/codes/102109.md | 5 ++ errors/codes/102110.md | 5 ++ errors/codes/102111.md | 5 ++ errors/codes/102112.md | 5 ++ errors/codes/102113.md | 5 ++ errors/codes/102200.md | 5 ++ errors/codes/102201.md | 5 ++ errors/codes/102202.md | 5 ++ errors/codes/103000.md | 5 ++ errors/codes/103001.md | 5 ++ errors/codes/103002.md | 5 ++ errors/codes/103003.md | 5 ++ errors/codes/103004.md | 5 ++ errors/codes/103005.md | 5 ++ errors/codes/103006.md | 5 ++ errors/codes/103007.md | 5 ++ errors/codes/103008.md | 5 ++ errors/codes/20000.md | 5 ++ errors/codes/40000.md | 5 ++ errors/codes/40001.md | 5 ++ errors/codes/40002.md | 5 ++ errors/codes/40003.md | 5 ++ errors/codes/40004.md | 5 ++ errors/codes/40005.md | 5 ++ errors/codes/40006.md | 5 ++ errors/codes/40007.md | 5 ++ errors/codes/40008.md | 5 ++ errors/codes/40009.md | 21 +++++++ errors/codes/40010.md | 5 ++ errors/codes/40011.md | 5 ++ errors/codes/40012.md | 5 ++ errors/codes/40013.md | 5 ++ errors/codes/40014.md | 5 ++ errors/codes/40015.md | 5 ++ errors/codes/40016.md | 5 ++ errors/codes/40017.md | 5 ++ errors/codes/40018.md | 5 ++ errors/codes/40019.md | 5 ++ errors/codes/40020.md | 5 ++ errors/codes/40021.md | 5 ++ errors/codes/40022.md | 5 ++ errors/codes/40023.md | 5 ++ errors/codes/40024.md | 25 ++++++++ errors/codes/40025.md | 5 ++ errors/codes/40030.md | 5 ++ errors/codes/40031.md | 5 ++ errors/codes/40032.md | 5 ++ errors/codes/40033.md | 17 +++++ errors/codes/40034.md | 5 ++ errors/codes/40035.md | 5 ++ errors/codes/40099.md | 5 ++ errors/codes/40100.md | 5 ++ errors/codes/40101.md | 5 ++ errors/codes/40102.md | 5 ++ errors/codes/40103.md | 5 ++ errors/codes/40104.md | 5 ++ errors/codes/40105.md | 5 ++ errors/codes/40106.md | 5 ++ errors/codes/40110.md | 5 ++ errors/codes/40111.md | 5 ++ errors/codes/40112.md | 5 ++ errors/codes/40113.md | 5 ++ errors/codes/40114.md | 5 ++ errors/codes/40115.md | 5 ++ errors/codes/40120.md | 5 ++ errors/codes/40121.md | 5 ++ errors/codes/40125.md | 5 ++ errors/codes/40126.md | 5 ++ errors/codes/40127.md | 5 ++ errors/codes/40130.md | 5 ++ errors/codes/40131.md | 5 ++ errors/codes/40132.md | 5 ++ errors/codes/40133.md | 5 ++ errors/codes/40140.md | 5 ++ errors/codes/40141.md | 5 ++ errors/codes/40142.md | 24 ++++++++ errors/codes/40143.md | 19 ++++++ errors/codes/40144.md | 5 ++ errors/codes/40145.md | 5 ++ errors/codes/40150.md | 5 ++ errors/codes/40160.md | 23 +++++++ errors/codes/40161.md | 5 ++ errors/codes/40162.md | 5 ++ errors/codes/40163.md | 5 ++ errors/codes/40164.md | 5 ++ errors/codes/40165.md | 19 ++++++ errors/codes/40166.md | 17 +++++ errors/codes/40170.md | 5 ++ errors/codes/40171.md | 5 ++ errors/codes/40172.md | 5 ++ errors/codes/40180.md | 5 ++ errors/codes/40181.md | 5 ++ errors/codes/40300.md | 5 ++ errors/codes/40310.md | 5 ++ errors/codes/40311.md | 5 ++ errors/codes/40320.md | 5 ++ errors/codes/40330.md | 5 ++ errors/codes/40331.md | 5 ++ errors/codes/40332.md | 5 ++ errors/codes/40400.md | 5 ++ errors/codes/40500.md | 5 ++ errors/codes/40900.md | 5 ++ errors/codes/41001.md | 5 ++ errors/codes/42200.md | 5 ++ errors/codes/42210.md | 5 ++ errors/codes/42211.md | 5 ++ errors/codes/42212.md | 5 ++ errors/codes/42213.md | 5 ++ errors/codes/42910.md | 17 +++++ errors/codes/42911.md | 19 ++++++ errors/codes/42912.md | 17 +++++ errors/codes/42913.md | 21 +++++++ errors/codes/42914.md | 21 +++++++ errors/codes/42915.md | 22 +++++++ errors/codes/42916.md | 22 +++++++ errors/codes/42917.md | 19 ++++++ errors/codes/42918.md | 19 ++++++ errors/codes/42920.md | 17 +++++ errors/codes/42921.md | 17 +++++ errors/codes/42922.md | 17 +++++ errors/codes/42923.md | 22 +++++++ errors/codes/42924.md | 17 +++++ errors/codes/42925.md | 19 ++++++ errors/codes/42926.md | 19 ++++++ errors/codes/50000.md | 5 ++ errors/codes/50001.md | 5 ++ errors/codes/50002.md | 5 ++ errors/codes/50003.md | 5 ++ errors/codes/50004.md | 5 ++ errors/codes/50005.md | 5 ++ errors/codes/50006.md | 5 ++ errors/codes/50010.md | 5 ++ errors/codes/50210.md | 5 ++ errors/codes/50310.md | 5 ++ errors/codes/50320.md | 5 ++ errors/codes/50330.md | 5 ++ errors/codes/50410.md | 5 ++ errors/codes/70000.md | 5 ++ errors/codes/70001.md | 19 ++++++ errors/codes/70002.md | 19 ++++++ errors/codes/70003.md | 5 ++ errors/codes/70004.md | 5 ++ errors/codes/70005.md | 5 ++ errors/codes/70006.md | 5 ++ errors/codes/71000.md | 5 ++ errors/codes/71001.md | 5 ++ errors/codes/71100.md | 5 ++ errors/codes/71101.md | 5 ++ errors/codes/71102.md | 5 ++ errors/codes/71200.md | 5 ++ errors/codes/71201.md | 5 ++ errors/codes/71202.md | 5 ++ errors/codes/71203.md | 5 ++ errors/codes/71204.md | 5 ++ errors/codes/71300.md | 5 ++ errors/codes/71301.md | 5 ++ errors/codes/71302.md | 5 ++ errors/codes/71303.md | 5 ++ errors/codes/72000.md | 5 ++ errors/codes/72001.md | 5 ++ errors/codes/72002.md | 23 +++++++ errors/codes/72003.md | 5 ++ errors/codes/72004.md | 5 ++ errors/codes/72005.md | 5 ++ errors/codes/72006.md | 5 ++ errors/codes/72007.md | 5 ++ errors/codes/72008.md | 5 ++ errors/codes/80000.md | 5 ++ errors/codes/80001.md | 5 ++ errors/codes/80002.md | 5 ++ errors/codes/80003.md | 19 ++++++ errors/codes/80004.md | 5 ++ errors/codes/80005.md | 5 ++ errors/codes/80006.md | 5 ++ errors/codes/80007.md | 5 ++ errors/codes/80008.md | 19 ++++++ errors/codes/80009.md | 5 ++ errors/codes/80010.md | 5 ++ errors/codes/80011.md | 5 ++ errors/codes/80012.md | 5 ++ errors/codes/80013.md | 5 ++ errors/codes/80014.md | 5 ++ errors/codes/80015.md | 5 ++ errors/codes/80016.md | 5 ++ errors/codes/80017.md | 5 ++ errors/codes/80018.md | 19 ++++++ errors/codes/80019.md | 5 ++ errors/codes/80020.md | 22 +++++++ errors/codes/80021.md | 5 ++ errors/codes/80022.md | 5 ++ errors/codes/80023.md | 21 +++++++ errors/codes/80024.md | 22 +++++++ errors/codes/80030.md | 5 ++ errors/codes/90000.md | 5 ++ errors/codes/90001.md | 5 ++ errors/codes/90002.md | 5 ++ errors/codes/90003.md | 22 +++++++ errors/codes/90004.md | 22 +++++++ errors/codes/90005.md | 22 +++++++ errors/codes/90006.md | 5 ++ errors/codes/90007.md | 5 ++ errors/codes/90008.md | 29 +++++++++ errors/codes/90009.md | 5 ++ errors/codes/90010.md | 5 ++ errors/codes/90021.md | 5 ++ errors/codes/91000.md | 5 ++ errors/codes/91001.md | 5 ++ errors/codes/91002.md | 5 ++ errors/codes/91003.md | 5 ++ errors/codes/91004.md | 5 ++ errors/codes/91005.md | 5 ++ errors/codes/91006.md | 5 ++ errors/codes/91007.md | 19 ++++++ errors/codes/91008.md | 5 ++ errors/codes/91100.md | 5 ++ errors/codes/92000.md | 5 ++ errors/codes/92001.md | 5 ++ errors/codes/92002.md | 5 ++ errors/codes/92003.md | 5 ++ errors/codes/92004.md | 5 ++ errors/codes/92005.md | 5 ++ errors/codes/92006.md | 5 ++ errors/codes/92007.md | 5 ++ errors/codes/92008.md | 5 ++ errors/codes/93001.md | 5 ++ errors/codes/93002.md | 5 ++ errors/guidelines.md | 137 +++++++++++++++++++++++++++++++++++++++++ 255 files changed, 2033 insertions(+) create mode 100644 errors/CLAUDE.md create mode 100644 errors/README.md create mode 100644 errors/codes/10000.md create mode 100644 errors/codes/101000.md create mode 100644 errors/codes/101001.md create mode 100644 errors/codes/101002.md create mode 100644 errors/codes/101003.md create mode 100644 errors/codes/101004.md create mode 100644 errors/codes/102000.md create mode 100644 errors/codes/102001.md create mode 100644 errors/codes/102002.md create mode 100644 errors/codes/102003.md create mode 100644 errors/codes/102004.md create mode 100644 errors/codes/102005.md create mode 100644 errors/codes/102050.md create mode 100644 errors/codes/102051.md create mode 100644 errors/codes/102052.md create mode 100644 errors/codes/102053.md create mode 100644 errors/codes/102054.md create mode 100644 errors/codes/102100.md create mode 100644 errors/codes/102101.md create mode 100644 errors/codes/102102.md create mode 100644 errors/codes/102103.md create mode 100644 errors/codes/102104.md create mode 100644 errors/codes/102105.md create mode 100644 errors/codes/102106.md create mode 100644 errors/codes/102107.md create mode 100644 errors/codes/102108.md create mode 100644 errors/codes/102109.md create mode 100644 errors/codes/102110.md create mode 100644 errors/codes/102111.md create mode 100644 errors/codes/102112.md create mode 100644 errors/codes/102113.md create mode 100644 errors/codes/102200.md create mode 100644 errors/codes/102201.md create mode 100644 errors/codes/102202.md create mode 100644 errors/codes/103000.md create mode 100644 errors/codes/103001.md create mode 100644 errors/codes/103002.md create mode 100644 errors/codes/103003.md create mode 100644 errors/codes/103004.md create mode 100644 errors/codes/103005.md create mode 100644 errors/codes/103006.md create mode 100644 errors/codes/103007.md create mode 100644 errors/codes/103008.md create mode 100644 errors/codes/20000.md create mode 100644 errors/codes/40000.md create mode 100644 errors/codes/40001.md create mode 100644 errors/codes/40002.md create mode 100644 errors/codes/40003.md create mode 100644 errors/codes/40004.md create mode 100644 errors/codes/40005.md create mode 100644 errors/codes/40006.md create mode 100644 errors/codes/40007.md create mode 100644 errors/codes/40008.md create mode 100644 errors/codes/40009.md create mode 100644 errors/codes/40010.md create mode 100644 errors/codes/40011.md create mode 100644 errors/codes/40012.md create mode 100644 errors/codes/40013.md create mode 100644 errors/codes/40014.md create mode 100644 errors/codes/40015.md create mode 100644 errors/codes/40016.md create mode 100644 errors/codes/40017.md create mode 100644 errors/codes/40018.md create mode 100644 errors/codes/40019.md create mode 100644 errors/codes/40020.md create mode 100644 errors/codes/40021.md create mode 100644 errors/codes/40022.md create mode 100644 errors/codes/40023.md create mode 100644 errors/codes/40024.md create mode 100644 errors/codes/40025.md create mode 100644 errors/codes/40030.md create mode 100644 errors/codes/40031.md create mode 100644 errors/codes/40032.md create mode 100644 errors/codes/40033.md create mode 100644 errors/codes/40034.md create mode 100644 errors/codes/40035.md create mode 100644 errors/codes/40099.md create mode 100644 errors/codes/40100.md create mode 100644 errors/codes/40101.md create mode 100644 errors/codes/40102.md create mode 100644 errors/codes/40103.md create mode 100644 errors/codes/40104.md create mode 100644 errors/codes/40105.md create mode 100644 errors/codes/40106.md create mode 100644 errors/codes/40110.md create mode 100644 errors/codes/40111.md create mode 100644 errors/codes/40112.md create mode 100644 errors/codes/40113.md create mode 100644 errors/codes/40114.md create mode 100644 errors/codes/40115.md create mode 100644 errors/codes/40120.md create mode 100644 errors/codes/40121.md create mode 100644 errors/codes/40125.md create mode 100644 errors/codes/40126.md create mode 100644 errors/codes/40127.md create mode 100644 errors/codes/40130.md create mode 100644 errors/codes/40131.md create mode 100644 errors/codes/40132.md create mode 100644 errors/codes/40133.md create mode 100644 errors/codes/40140.md create mode 100644 errors/codes/40141.md create mode 100644 errors/codes/40142.md create mode 100644 errors/codes/40143.md create mode 100644 errors/codes/40144.md create mode 100644 errors/codes/40145.md create mode 100644 errors/codes/40150.md create mode 100644 errors/codes/40160.md create mode 100644 errors/codes/40161.md create mode 100644 errors/codes/40162.md create mode 100644 errors/codes/40163.md create mode 100644 errors/codes/40164.md create mode 100644 errors/codes/40165.md create mode 100644 errors/codes/40166.md create mode 100644 errors/codes/40170.md create mode 100644 errors/codes/40171.md create mode 100644 errors/codes/40172.md create mode 100644 errors/codes/40180.md create mode 100644 errors/codes/40181.md create mode 100644 errors/codes/40300.md create mode 100644 errors/codes/40310.md create mode 100644 errors/codes/40311.md create mode 100644 errors/codes/40320.md create mode 100644 errors/codes/40330.md create mode 100644 errors/codes/40331.md create mode 100644 errors/codes/40332.md create mode 100644 errors/codes/40400.md create mode 100644 errors/codes/40500.md create mode 100644 errors/codes/40900.md create mode 100644 errors/codes/41001.md create mode 100644 errors/codes/42200.md create mode 100644 errors/codes/42210.md create mode 100644 errors/codes/42211.md create mode 100644 errors/codes/42212.md create mode 100644 errors/codes/42213.md create mode 100644 errors/codes/42910.md create mode 100644 errors/codes/42911.md create mode 100644 errors/codes/42912.md create mode 100644 errors/codes/42913.md create mode 100644 errors/codes/42914.md create mode 100644 errors/codes/42915.md create mode 100644 errors/codes/42916.md create mode 100644 errors/codes/42917.md create mode 100644 errors/codes/42918.md create mode 100644 errors/codes/42920.md create mode 100644 errors/codes/42921.md create mode 100644 errors/codes/42922.md create mode 100644 errors/codes/42923.md create mode 100644 errors/codes/42924.md create mode 100644 errors/codes/42925.md create mode 100644 errors/codes/42926.md create mode 100644 errors/codes/50000.md create mode 100644 errors/codes/50001.md create mode 100644 errors/codes/50002.md create mode 100644 errors/codes/50003.md create mode 100644 errors/codes/50004.md create mode 100644 errors/codes/50005.md create mode 100644 errors/codes/50006.md create mode 100644 errors/codes/50010.md create mode 100644 errors/codes/50210.md create mode 100644 errors/codes/50310.md create mode 100644 errors/codes/50320.md create mode 100644 errors/codes/50330.md create mode 100644 errors/codes/50410.md create mode 100644 errors/codes/70000.md create mode 100644 errors/codes/70001.md create mode 100644 errors/codes/70002.md create mode 100644 errors/codes/70003.md create mode 100644 errors/codes/70004.md create mode 100644 errors/codes/70005.md create mode 100644 errors/codes/70006.md create mode 100644 errors/codes/71000.md create mode 100644 errors/codes/71001.md create mode 100644 errors/codes/71100.md create mode 100644 errors/codes/71101.md create mode 100644 errors/codes/71102.md create mode 100644 errors/codes/71200.md create mode 100644 errors/codes/71201.md create mode 100644 errors/codes/71202.md create mode 100644 errors/codes/71203.md create mode 100644 errors/codes/71204.md create mode 100644 errors/codes/71300.md create mode 100644 errors/codes/71301.md create mode 100644 errors/codes/71302.md create mode 100644 errors/codes/71303.md create mode 100644 errors/codes/72000.md create mode 100644 errors/codes/72001.md create mode 100644 errors/codes/72002.md create mode 100644 errors/codes/72003.md create mode 100644 errors/codes/72004.md create mode 100644 errors/codes/72005.md create mode 100644 errors/codes/72006.md create mode 100644 errors/codes/72007.md create mode 100644 errors/codes/72008.md create mode 100644 errors/codes/80000.md create mode 100644 errors/codes/80001.md create mode 100644 errors/codes/80002.md create mode 100644 errors/codes/80003.md create mode 100644 errors/codes/80004.md create mode 100644 errors/codes/80005.md create mode 100644 errors/codes/80006.md create mode 100644 errors/codes/80007.md create mode 100644 errors/codes/80008.md create mode 100644 errors/codes/80009.md create mode 100644 errors/codes/80010.md create mode 100644 errors/codes/80011.md create mode 100644 errors/codes/80012.md create mode 100644 errors/codes/80013.md create mode 100644 errors/codes/80014.md create mode 100644 errors/codes/80015.md create mode 100644 errors/codes/80016.md create mode 100644 errors/codes/80017.md create mode 100644 errors/codes/80018.md create mode 100644 errors/codes/80019.md create mode 100644 errors/codes/80020.md create mode 100644 errors/codes/80021.md create mode 100644 errors/codes/80022.md create mode 100644 errors/codes/80023.md create mode 100644 errors/codes/80024.md create mode 100644 errors/codes/80030.md create mode 100644 errors/codes/90000.md create mode 100644 errors/codes/90001.md create mode 100644 errors/codes/90002.md create mode 100644 errors/codes/90003.md create mode 100644 errors/codes/90004.md create mode 100644 errors/codes/90005.md create mode 100644 errors/codes/90006.md create mode 100644 errors/codes/90007.md create mode 100644 errors/codes/90008.md create mode 100644 errors/codes/90009.md create mode 100644 errors/codes/90010.md create mode 100644 errors/codes/90021.md create mode 100644 errors/codes/91000.md create mode 100644 errors/codes/91001.md create mode 100644 errors/codes/91002.md create mode 100644 errors/codes/91003.md create mode 100644 errors/codes/91004.md create mode 100644 errors/codes/91005.md create mode 100644 errors/codes/91006.md create mode 100644 errors/codes/91007.md create mode 100644 errors/codes/91008.md create mode 100644 errors/codes/91100.md create mode 100644 errors/codes/92000.md create mode 100644 errors/codes/92001.md create mode 100644 errors/codes/92002.md create mode 100644 errors/codes/92003.md create mode 100644 errors/codes/92004.md create mode 100644 errors/codes/92005.md create mode 100644 errors/codes/92006.md create mode 100644 errors/codes/92007.md create mode 100644 errors/codes/92008.md create mode 100644 errors/codes/93001.md create mode 100644 errors/codes/93002.md create mode 100644 errors/guidelines.md diff --git a/errors/CLAUDE.md b/errors/CLAUDE.md new file mode 100644 index 00000000..51e88920 --- /dev/null +++ b/errors/CLAUDE.md @@ -0,0 +1,27 @@ +# Error code documentation + +**Before adding, editing, or reviewing any `.md` file in this directory, read [`guidelines.md`](./guidelines.md).** The rules on title/summary length, tone, and terminology are strict and not all obvious from reading existing entries. + +## What this directory holds + +The per-code files live in [`codes/`](./codes): one Markdown file per error code, named `.md` (e.g. `40142.md`). Each file has YAML frontmatter (`code`, `title`, `summary`) and an optional Markdown body that provides the detail-page content beneath the title and summary. Title, summary, and body conventions are all covered in `guidelines.md`. + +The set of files in `codes/` **is the registry** — the source of truth for which codes are valid and what they mean. `protocol/errors.json` is the legacy `code → title` map this registry supersedes; it is being retired. While it still exists, the validator (`scripts/`) checks that every `errors.json` code has a matching `codes/.md`, so keep the two aligned when adding a code. + +## Conventions at a glance + +These are reminders, not substitutes for `guidelines.md`. + +- **Filename = code.** `40142.md` must have `code: 40142` in frontmatter. +- **Title** — 3–7 words (cap 10), short phrase or concise subject–verb clause (not necessarily a noun phrase), sentence case, no trailing punctuation, no error-code restatement. Specific enough that a reader can distinguish it from neighbouring errors at a glance. +- **Summary** — 15–40 words, one or two sentences, plain language. Describe what happened, then (when known) why it might have happened. Don't prescribe remediation — no "Check…", "Verify…", "Retry…". Generic phrasing — the summary represents *every* occurrence of the code, so avoid deictic ("this token", "your channel"); prefer "the token", "the channel". No Markdown formatting, stack traces, or internal identifiers. +- **Body** (optional) — detail-page content beneath the title and summary. Don't restate either. Order: *What you should do* (lead with the triage — including when the answer is "nothing", for transient/self-healing errors) → *Why it happens* (causes in the reader's terms, each with its fix) → *What you'll see* (message string(s) and status, last, as a findability aid). Link out to feature docs for how-to; no inline code samples. No "related errors" section — disambiguate inline only when codes are genuinely confusable. Describe generically, instruct in second person. +- **YAML quoting** — single-line summaries don't need quoting unless they start with a special character (`:`, `-`, `[`, `{`, `#`, `&`, `*`, `!`, `|`, `>`, `'`, `"`, `%`, `@`, `` ` ``). In that case wrap the whole summary in double quotes. +- **Terminology** — use the customer-facing terms from the [Dictionary of terms](https://ably.atlassian.net/wiki/spaces/devex/pages/4295262228/Dictionary+of+terms). Common substitutions: *Region* not site/datacenter/cluster; *Ably Pub/Sub JavaScript SDK* not client library; *Integration* not Reactor/Firehose/integration rule ("rule" now means channel rule); *Token / Basic authentication* not key authentication; *Connection state recovery* not stream resume. +- **Tone** — plain, calm, specific. Treat copy as product copy, not log messages. Aim for a single consistent voice across the whole directory. + +## Reviewing a change + +1. Read the table view in your head — does the title disambiguate this error from neighbours? Does the summary stand alone? +2. Open two or three existing files side-by-side and check that tone, length, and structure match. +3. Check that the filename matches `code:` and that required fields are present. diff --git a/errors/README.md b/errors/README.md new file mode 100644 index 00000000..0c4aaad8 --- /dev/null +++ b/errors/README.md @@ -0,0 +1,32 @@ +# Error documentation + +This directory is the canonical registry of Ably's error codes and their customer-facing documentation. It is the source of truth for both **which codes are valid** and **what each one means**, used across the wider Ably stack. The registry lives in [`codes/`](./codes) — one Markdown file per valid code — so "is code X valid?" is answered by "does `codes/.md` exist?". + +## Registering a new error code + +To register a new code, create a Markdown file at `codes/.md` (the filename must be the numeric code, e.g. `codes/40010.md`). It must start with YAML frontmatter containing three required fields: + +```markdown +--- +code: 40010 +title: Invalid channel name +summary: The channel name in the request was not valid, for example because it was empty, contained characters that are not permitted, or used an unknown square-bracketed prefix. +--- +``` + +- **`code`** — the numeric error code. Must match the filename. +- **`title`** — a short phrase identifying the error at a glance (aim for 3–7 words), e.g. `Invalid channel name`. +- **`summary`** — one or two plain-language sentences (roughly 15–40 words) covering what happened and, when known, why. + +You can add an optional Markdown body beneath the frontmatter for detail-page content (what to do, why it happens). While `protocol/errors.json` still exists, add the same code there too — the validator requires every `errors.json` code to have a registry file. + +See [`guidelines.md`](./guidelines.md) for the full rules on title, summary, body, tone, and terminology, and run `npm run validate:errors` to check your entry (CI runs it automatically). + +## What's in this directory + +- [`codes/`](./codes) — the registry: one `.md` per valid code. +- [`guidelines.md`](./guidelines.md) — how to write entries: rules on title, summary, body, tone, and terminology. +- [`CLAUDE.md`](./CLAUDE.md) — guidance for agents adding, editing, or reviewing entries. +- [`scripts/`](./scripts) — the validator run in CI. + +`protocol/errors.json` is the legacy `code → title` map that this registry supersedes; it is being retired once every consumer has moved off it. Until then, the validator checks that the registry and `errors.json` stay aligned. diff --git a/errors/codes/10000.md b/errors/codes/10000.md new file mode 100644 index 00000000..fe98bbe0 --- /dev/null +++ b/errors/codes/10000.md @@ -0,0 +1,5 @@ +--- +code: 10000 +title: No error +summary: A placeholder code indicating success, used where an error code is expected but the operation completed normally. It does not represent a failure. +--- diff --git a/errors/codes/101000.md b/errors/codes/101000.md new file mode 100644 index 00000000..bc109cf9 --- /dev/null +++ b/errors/codes/101000.md @@ -0,0 +1,5 @@ +--- +code: 101000 +title: Space name is empty +summary: A space could not be created or referenced because no name was supplied. +--- diff --git a/errors/codes/101001.md b/errors/codes/101001.md new file mode 100644 index 00000000..f3400f93 --- /dev/null +++ b/errors/codes/101001.md @@ -0,0 +1,5 @@ +--- +code: 101001 +title: Space must be entered first +summary: An operation was attempted that requires having first entered the space. In Ably Spaces, actions such as updating a member's location or profile are only available once the space has been entered. +--- diff --git a/errors/codes/101002.md b/errors/codes/101002.md new file mode 100644 index 00000000..e3b44dc5 --- /dev/null +++ b/errors/codes/101002.md @@ -0,0 +1,5 @@ +--- +code: 101002 +title: Lock request already pending +summary: A lock could not be requested because a request for the same lock is already in progress. In Ably Spaces, a member may only have one outstanding request for a given lock at a time. +--- diff --git a/errors/codes/101003.md b/errors/codes/101003.md new file mode 100644 index 00000000..e4e3a189 --- /dev/null +++ b/errors/codes/101003.md @@ -0,0 +1,5 @@ +--- +code: 101003 +title: Lock already held +summary: A lock could not be acquired because it is currently held by another member. In Ably Spaces, only one member can hold a given lock at a time, and it must be released before another can take it. +--- diff --git a/errors/codes/101004.md b/errors/codes/101004.md new file mode 100644 index 00000000..f1c951cc --- /dev/null +++ b/errors/codes/101004.md @@ -0,0 +1,5 @@ +--- +code: 101004 +title: Lock invalidated by concurrent request +summary: A lock that appeared to be acquired was invalidated because another member requested the same lock concurrently and now holds it. +--- diff --git a/errors/codes/102000.md b/errors/codes/102000.md new file mode 100644 index 00000000..df9586ca --- /dev/null +++ b/errors/codes/102000.md @@ -0,0 +1,5 @@ +--- +code: 102000 +title: Invalid chat room name +summary: A Chat room could not be used because the name supplied for it was not valid. +--- diff --git a/errors/codes/102001.md b/errors/codes/102001.md new file mode 100644 index 00000000..ecf6c967 --- /dev/null +++ b/errors/codes/102001.md @@ -0,0 +1,5 @@ +--- +code: 102001 +title: Room attach failed for messages +summary: Attaching a Chat room failed because its messages feature could not be attached. +--- diff --git a/errors/codes/102002.md b/errors/codes/102002.md new file mode 100644 index 00000000..919bb942 --- /dev/null +++ b/errors/codes/102002.md @@ -0,0 +1,5 @@ +--- +code: 102002 +title: Room attach failed for presence +summary: Attaching a Chat room failed because its presence feature could not be attached. +--- diff --git a/errors/codes/102003.md b/errors/codes/102003.md new file mode 100644 index 00000000..1d358034 --- /dev/null +++ b/errors/codes/102003.md @@ -0,0 +1,5 @@ +--- +code: 102003 +title: Room attach failed for reactions +summary: Attaching a Chat room failed because its reactions feature could not be attached. +--- diff --git a/errors/codes/102004.md b/errors/codes/102004.md new file mode 100644 index 00000000..1fe75d17 --- /dev/null +++ b/errors/codes/102004.md @@ -0,0 +1,5 @@ +--- +code: 102004 +title: Room attach failed for occupancy +summary: Attaching a Chat room failed because its occupancy feature could not be attached. +--- diff --git a/errors/codes/102005.md b/errors/codes/102005.md new file mode 100644 index 00000000..9c4da8fc --- /dev/null +++ b/errors/codes/102005.md @@ -0,0 +1,5 @@ +--- +code: 102005 +title: Room attach failed for typing +summary: Attaching a Chat room failed because its typing feature could not be attached. +--- diff --git a/errors/codes/102050.md b/errors/codes/102050.md new file mode 100644 index 00000000..3b8eaab5 --- /dev/null +++ b/errors/codes/102050.md @@ -0,0 +1,5 @@ +--- +code: 102050 +title: Room detach failed for messages +summary: Detaching a Chat room failed because its messages feature could not be detached. +--- diff --git a/errors/codes/102051.md b/errors/codes/102051.md new file mode 100644 index 00000000..36eb3704 --- /dev/null +++ b/errors/codes/102051.md @@ -0,0 +1,5 @@ +--- +code: 102051 +title: Room detach failed for presence +summary: Detaching a Chat room failed because its presence feature could not be detached. +--- diff --git a/errors/codes/102052.md b/errors/codes/102052.md new file mode 100644 index 00000000..e414fc7d --- /dev/null +++ b/errors/codes/102052.md @@ -0,0 +1,5 @@ +--- +code: 102052 +title: Room detach failed for reactions +summary: Detaching a Chat room failed because its reactions feature could not be detached. +--- diff --git a/errors/codes/102053.md b/errors/codes/102053.md new file mode 100644 index 00000000..152e690f --- /dev/null +++ b/errors/codes/102053.md @@ -0,0 +1,5 @@ +--- +code: 102053 +title: Room detach failed for occupancy +summary: Detaching a Chat room failed because its occupancy feature could not be detached. +--- diff --git a/errors/codes/102054.md b/errors/codes/102054.md new file mode 100644 index 00000000..4e6ef0f0 --- /dev/null +++ b/errors/codes/102054.md @@ -0,0 +1,5 @@ +--- +code: 102054 +title: Room detach failed for typing +summary: Detaching a Chat room failed because its typing feature could not be detached. +--- diff --git a/errors/codes/102100.md b/errors/codes/102100.md new file mode 100644 index 00000000..c6e88bfc --- /dev/null +++ b/errors/codes/102100.md @@ -0,0 +1,5 @@ +--- +code: 102100 +title: Room continuity not guaranteed +summary: A discontinuity was detected on the Chat room, so the continuity of messages could not be guaranteed. This usually follows a disconnection long enough that the room could not resume from where it left off. +--- diff --git a/errors/codes/102101.md b/errors/codes/102101.md new file mode 100644 index 00000000..aeafad87 --- /dev/null +++ b/errors/codes/102101.md @@ -0,0 +1,5 @@ +--- +code: 102101 +title: Room is in the failed state +summary: The operation could not be performed because the Chat room is in the failed state. A room enters this state when an earlier operation on it failed and could not recover. +--- diff --git a/errors/codes/102102.md b/errors/codes/102102.md new file mode 100644 index 00000000..fe9dac35 --- /dev/null +++ b/errors/codes/102102.md @@ -0,0 +1,5 @@ +--- +code: 102102 +title: Room is being released +summary: The operation could not be performed because the Chat room is in the process of being released. +--- diff --git a/errors/codes/102103.md b/errors/codes/102103.md new file mode 100644 index 00000000..5d335c73 --- /dev/null +++ b/errors/codes/102103.md @@ -0,0 +1,5 @@ +--- +code: 102103 +title: Room has been released +summary: The operation could not be performed because the Chat room has already been released. +--- diff --git a/errors/codes/102104.md b/errors/codes/102104.md new file mode 100644 index 00000000..8158f974 --- /dev/null +++ b/errors/codes/102104.md @@ -0,0 +1,5 @@ +--- +code: 102104 +title: Room operation failed after earlier attempt +summary: The operation failed because a preceding attempt to perform it had already failed. +--- diff --git a/errors/codes/102105.md b/errors/codes/102105.md new file mode 100644 index 00000000..d27a9b8d --- /dev/null +++ b/errors/codes/102105.md @@ -0,0 +1,5 @@ +--- +code: 102105 +title: Unknown room lifecycle error +summary: An unexpected error occurred in the Chat room lifecycle that does not correspond to a known condition. +--- diff --git a/errors/codes/102106.md b/errors/codes/102106.md new file mode 100644 index 00000000..f8c4b143 --- /dev/null +++ b/errors/codes/102106.md @@ -0,0 +1,5 @@ +--- +code: 102106 +title: Room released before operation completed +summary: The operation could not complete because the Chat room was released while it was still in progress. +--- diff --git a/errors/codes/102107.md b/errors/codes/102107.md new file mode 100644 index 00000000..df7126ab --- /dev/null +++ b/errors/codes/102107.md @@ -0,0 +1,5 @@ +--- +code: 102107 +title: Room already exists with different options +summary: A Chat room with the same name already exists but was created with different options. A room must be released before it can be requested again with a different configuration. +--- diff --git a/errors/codes/102108.md b/errors/codes/102108.md new file mode 100644 index 00000000..392deeea --- /dev/null +++ b/errors/codes/102108.md @@ -0,0 +1,5 @@ +--- +code: 102108 +title: Feature not enabled in room options +summary: The operation required a feature that was not enabled when the Chat room was created. Features such as presence or typing indicators must be enabled in the room options to be used. +--- diff --git a/errors/codes/102109.md b/errors/codes/102109.md new file mode 100644 index 00000000..6727a471 --- /dev/null +++ b/errors/codes/102109.md @@ -0,0 +1,5 @@ +--- +code: 102109 +title: History requested for an unsubscribed listener +summary: A request for the messages published before a listener's subscription point was rejected because that listener was not subscribed. This history is only available while the listener is subscribed to the room. +--- diff --git a/errors/codes/102110.md b/errors/codes/102110.md new file mode 100644 index 00000000..67d04ed8 --- /dev/null +++ b/errors/codes/102110.md @@ -0,0 +1,5 @@ +--- +code: 102110 +title: Channel serial not defined +summary: A channel serial was expected but was not defined at the point it was needed. +--- diff --git a/errors/codes/102111.md b/errors/codes/102111.md new file mode 100644 index 00000000..4a07cd41 --- /dev/null +++ b/errors/codes/102111.md @@ -0,0 +1,5 @@ +--- +code: 102111 +title: Channel options modified after request +summary: Channel options could not be changed because the underlying channel had already been requested. +--- diff --git a/errors/codes/102112.md b/errors/codes/102112.md new file mode 100644 index 00000000..02273a7e --- /dev/null +++ b/errors/codes/102112.md @@ -0,0 +1,5 @@ +--- +code: 102112 +title: Room is in an invalid state +summary: The operation could not be performed because the Chat room was in a state that does not permit it. +--- diff --git a/errors/codes/102113.md b/errors/codes/102113.md new file mode 100644 index 00000000..07819ff5 --- /dev/null +++ b/errors/codes/102113.md @@ -0,0 +1,5 @@ +--- +code: 102113 +title: Sequential execution could not be enforced +summary: The operation failed because its sequential execution could not be enforced. Chat room lifecycle operations are expected to run one at a time, and that ordering could not be guaranteed. +--- diff --git a/errors/codes/102200.md b/errors/codes/102200.md new file mode 100644 index 00000000..8851a715 --- /dev/null +++ b/errors/codes/102200.md @@ -0,0 +1,5 @@ +--- +code: 102200 +title: Hook used outside its provider +summary: An Ably Chat React hook was used in a component that is not wrapped in the required provider. The hooks depend on context supplied by a provider higher in the component tree. +--- diff --git a/errors/codes/102201.md b/errors/codes/102201.md new file mode 100644 index 00000000..4e544021 --- /dev/null +++ b/errors/codes/102201.md @@ -0,0 +1,5 @@ +--- +code: 102201 +title: Component unmounted before completion +summary: An operation started by the Ably Chat React hooks could not finish because the React component was unmounted while it was still in progress. +--- diff --git a/errors/codes/102202.md b/errors/codes/102202.md new file mode 100644 index 00000000..99bdb40b --- /dev/null +++ b/errors/codes/102202.md @@ -0,0 +1,5 @@ +--- +code: 102202 +title: Presence data could not be fetched +summary: The Ably Chat React hooks could not retrieve presence data after the maximum number of retries. This usually points to a persistent problem reaching presence rather than a transient one. +--- diff --git a/errors/codes/103000.md b/errors/codes/103000.md new file mode 100644 index 00000000..6e3b857b --- /dev/null +++ b/errors/codes/103000.md @@ -0,0 +1,5 @@ +--- +code: 103000 +title: Push notification internal error +summary: The push notification was not delivered because of an unexpected error within the Ably platform during preparation, dispatch, or requeue. The cause is not related to the contents of the request or the device's registration. +--- diff --git a/errors/codes/103001.md b/errors/codes/103001.md new file mode 100644 index 00000000..18865ebd --- /dev/null +++ b/errors/codes/103001.md @@ -0,0 +1,5 @@ +--- +code: 103001 +title: Push notification retry limit reached +summary: The push notification was not delivered because the configured retry limit was reached after repeated unsuccessful delivery attempts. The underlying failures are typically transient — provider outages, rate limits, or network errors. +--- diff --git a/errors/codes/103002.md b/errors/codes/103002.md new file mode 100644 index 00000000..e4d2be00 --- /dev/null +++ b/errors/codes/103002.md @@ -0,0 +1,5 @@ +--- +code: 103002 +title: Push notification recipient missing +summary: The push notification was not delivered because the direct push request was submitted without a recipient device. Direct push targets a single device by ID and cannot be processed when no recipient is supplied. +--- diff --git a/errors/codes/103003.md b/errors/codes/103003.md new file mode 100644 index 00000000..61f74f03 --- /dev/null +++ b/errors/codes/103003.md @@ -0,0 +1,5 @@ +--- +code: 103003 +title: Push notification body invalid +summary: The push notification was not delivered because its body could not be processed. This typically indicates a malformed or unsupported message structure in the publish request. +--- diff --git a/errors/codes/103004.md b/errors/codes/103004.md new file mode 100644 index 00000000..c2d32381 --- /dev/null +++ b/errors/codes/103004.md @@ -0,0 +1,5 @@ +--- +code: 103004 +title: Push notification rejected by provider +summary: The push notification was not delivered because the provider — APNs, FCM, or WebPush — rejected the request due to a problem with the payload or delivery parameters. Typical causes include an oversized payload, a disallowed topic, or an unsupported field. +--- diff --git a/errors/codes/103005.md b/errors/codes/103005.md new file mode 100644 index 00000000..8f2cec27 --- /dev/null +++ b/errors/codes/103005.md @@ -0,0 +1,5 @@ +--- +code: 103005 +title: Push device token invalid +summary: The push notification was not delivered because the provider — APNs, FCM, or WebPush — rejected the device's push token as invalid for the configured application. The token may have been issued under different credentials or for a different application. +--- diff --git a/errors/codes/103006.md b/errors/codes/103006.md new file mode 100644 index 00000000..b3840b9a --- /dev/null +++ b/errors/codes/103006.md @@ -0,0 +1,5 @@ +--- +code: 103006 +title: Push transport not configured +summary: The push notification was not delivered because the device's push notification provider — APNs, FCM, or WebPush — has no credentials configured on the application. No deliveries can be made through that provider until the credentials are present. +--- diff --git a/errors/codes/103007.md b/errors/codes/103007.md new file mode 100644 index 00000000..6d4eed0b --- /dev/null +++ b/errors/codes/103007.md @@ -0,0 +1,5 @@ +--- +code: 103007 +title: Push notification provider unreachable +summary: The push notification was not delivered because the provider — APNs, FCM, or WebPush — could not be reached, returned a server error, or sent a response that could not be processed. The condition is usually transient. +--- diff --git a/errors/codes/103008.md b/errors/codes/103008.md new file mode 100644 index 00000000..e7785676 --- /dev/null +++ b/errors/codes/103008.md @@ -0,0 +1,5 @@ +--- +code: 103008 +title: Push transport credentials invalid +summary: A push notification could not be sent because the credentials configured for the target platform were rejected or had expired, such as an expired APNs certificate. +--- diff --git a/errors/codes/20000.md b/errors/codes/20000.md new file mode 100644 index 00000000..f102fb44 --- /dev/null +++ b/errors/codes/20000.md @@ -0,0 +1,5 @@ +--- +code: 20000 +title: General error +summary: A generic error that does not correspond to a more specific code. It is used as a catch-all when the condition could not be classified more precisely. +--- diff --git a/errors/codes/40000.md b/errors/codes/40000.md new file mode 100644 index 00000000..34da8c08 --- /dev/null +++ b/errors/codes/40000.md @@ -0,0 +1,5 @@ +--- +code: 40000 +title: Bad request +summary: The request was rejected because it was invalid and could not be processed. +--- diff --git a/errors/codes/40001.md b/errors/codes/40001.md new file mode 100644 index 00000000..6a2d5510 --- /dev/null +++ b/errors/codes/40001.md @@ -0,0 +1,5 @@ +--- +code: 40001 +title: Invalid request body +summary: The body of the request could not be processed because it was invalid, missing required fields, or not in the expected format. +--- diff --git a/errors/codes/40002.md b/errors/codes/40002.md new file mode 100644 index 00000000..2400e5ea --- /dev/null +++ b/errors/codes/40002.md @@ -0,0 +1,5 @@ +--- +code: 40002 +title: Invalid request parameter name +summary: The request included a parameter that is not recognised or not permitted for the operation being performed. +--- diff --git a/errors/codes/40003.md b/errors/codes/40003.md new file mode 100644 index 00000000..c07a0480 --- /dev/null +++ b/errors/codes/40003.md @@ -0,0 +1,5 @@ +--- +code: 40003 +title: Invalid request parameter value +summary: A parameter in the request was invalid, such as a value of the wrong type, outside the allowed range, or otherwise unacceptable for that parameter. +--- diff --git a/errors/codes/40004.md b/errors/codes/40004.md new file mode 100644 index 00000000..7899ceba --- /dev/null +++ b/errors/codes/40004.md @@ -0,0 +1,5 @@ +--- +code: 40004 +title: Invalid request header +summary: A header supplied with the request was missing, invalid, or held a value that was not accepted for the operation. +--- diff --git a/errors/codes/40005.md b/errors/codes/40005.md new file mode 100644 index 00000000..a3ae5bdb --- /dev/null +++ b/errors/codes/40005.md @@ -0,0 +1,5 @@ +--- +code: 40005 +title: Invalid API key or token +summary: An API key or token supplied with the request could not be read because it was not in the expected form. This is distinct from a key or token that is well-formed but unauthorised. +--- diff --git a/errors/codes/40006.md b/errors/codes/40006.md new file mode 100644 index 00000000..7d8c2efe --- /dev/null +++ b/errors/codes/40006.md @@ -0,0 +1,5 @@ +--- +code: 40006 +title: Message contains invalid connection ID +summary: A published message referenced a connection by an ID or key that was invalid or did not match, so the message was rejected. +--- diff --git a/errors/codes/40007.md b/errors/codes/40007.md new file mode 100644 index 00000000..1dd603f2 --- /dev/null +++ b/errors/codes/40007.md @@ -0,0 +1,5 @@ +--- +code: 40007 +title: Invalid connection serial +summary: An older Ably SDK attempted to resume a connection using an invalid connection serial. +--- diff --git a/errors/codes/40008.md b/errors/codes/40008.md new file mode 100644 index 00000000..531a0f3d --- /dev/null +++ b/errors/codes/40008.md @@ -0,0 +1,5 @@ +--- +code: 40008 +title: Invalid content length +summary: More data arrived in the request body than its Content-Length header declared, so the request was rejected. +--- diff --git a/errors/codes/40009.md b/errors/codes/40009.md new file mode 100644 index 00000000..9e8efc78 --- /dev/null +++ b/errors/codes/40009.md @@ -0,0 +1,21 @@ +--- +code: 40009 +title: Message too large +summary: A message was rejected because it exceeded the maximum permitted size. When several messages are published in a single call, the limit applies to their combined size rather than to each one individually. +--- + +## What you should do + +Reduce the size of what you publish so it falls within the limit. Because it is rejected outright and not retried, it has to be made smaller to get through. If you publish several messages in a single call, the limit applies to their combined size, so sending fewer messages per call is one way to do that. + +The default maximum is 64 KB, but the exact figure depends on your account; check it against your account's [limits](https://ably.com/docs/general/limits). If your use case genuinely needs larger messages, you can request a higher limit. + +## Why it happens + +Every published message has a maximum size, set by your account's limits, and this error means the payload exceeded it. The size counts the message's data together with its name, client ID, and any extras, so all of these contribute to the total. Publishing an array of messages in one call is measured as a single payload, so a batch of individually small messages can still exceed the limit in aggregate. + +This is about total size in bytes. A publish rejected for containing too many messages, rather than too large a payload, is reported as 40034 instead. + +## What you'll see + +The publish is rejected and the message is not delivered. The error is reported with code 40009 and HTTP status 413, with a message of the form `Maximum message length exceeded (size was bytes, limit is bytes)`. diff --git a/errors/codes/40010.md b/errors/codes/40010.md new file mode 100644 index 00000000..842675dd --- /dev/null +++ b/errors/codes/40010.md @@ -0,0 +1,5 @@ +--- +code: 40010 +title: Invalid channel name +summary: The channel name in the request was not valid, for example because it was empty, contained characters that are not permitted, or used an unknown square-bracketed prefix. +--- diff --git a/errors/codes/40011.md b/errors/codes/40011.md new file mode 100644 index 00000000..dd022a47 --- /dev/null +++ b/errors/codes/40011.md @@ -0,0 +1,5 @@ +--- +code: 40011 +title: Pagination sequence no longer valid +summary: An enumeration query could not continue because the underlying set of results shifted between pages, so the pagination sequence was no longer valid. +--- diff --git a/errors/codes/40012.md b/errors/codes/40012.md new file mode 100644 index 00000000..b5f80824 --- /dev/null +++ b/errors/codes/40012.md @@ -0,0 +1,5 @@ +--- +code: 40012 +title: Invalid client ID +summary: The client ID supplied was not acceptable, for example because it was empty, a wildcard, not a string, or did not match the client ID permitted by the credentials in use. +--- diff --git a/errors/codes/40013.md b/errors/codes/40013.md new file mode 100644 index 00000000..49752ade --- /dev/null +++ b/errors/codes/40013.md @@ -0,0 +1,5 @@ +--- +code: 40013 +title: Invalid message data or encoding +summary: A message was rejected because its data was of an unsupported type, or because the encoding declared for the data could not be applied or reversed. +--- diff --git a/errors/codes/40014.md b/errors/codes/40014.md new file mode 100644 index 00000000..f8fe84c5 --- /dev/null +++ b/errors/codes/40014.md @@ -0,0 +1,5 @@ +--- +code: 40014 +title: Resource disposed +summary: The operation could not complete because an internal Ably resource it targeted was temporarily unavailable, typically during routine changes on Ably's side such as failover or rebalancing. +--- diff --git a/errors/codes/40015.md b/errors/codes/40015.md new file mode 100644 index 00000000..be492db1 --- /dev/null +++ b/errors/codes/40015.md @@ -0,0 +1,5 @@ +--- +code: 40015 +title: Invalid device ID +summary: The device ID supplied for a push notification operation was missing or not in an acceptable form, so the request was rejected. +--- diff --git a/errors/codes/40016.md b/errors/codes/40016.md new file mode 100644 index 00000000..76250410 --- /dev/null +++ b/errors/codes/40016.md @@ -0,0 +1,5 @@ +--- +code: 40016 +title: Invalid message name +summary: A message supplied a name that was invalid, so the message was rejected. +--- diff --git a/errors/codes/40017.md b/errors/codes/40017.md new file mode 100644 index 00000000..8f3100e1 --- /dev/null +++ b/errors/codes/40017.md @@ -0,0 +1,5 @@ +--- +code: 40017 +title: Unsupported protocol version +summary: The request specified a protocol version that Ably does not support, or omitted a version where one is required. The connection or request was rejected before processing. +--- diff --git a/errors/codes/40018.md b/errors/codes/40018.md new file mode 100644 index 00000000..548f004d --- /dev/null +++ b/errors/codes/40018.md @@ -0,0 +1,5 @@ +--- +code: 40018 +title: Message delta could not be applied +summary: On a channel using delta compression, the Ably SDK could not apply a message's delta against the preceding message — for example because that message was missed or arrived out of order. The SDK recovers automatically by reattaching the channel to receive a full message. +--- diff --git a/errors/codes/40019.md b/errors/codes/40019.md new file mode 100644 index 00000000..989851e2 --- /dev/null +++ b/errors/codes/40019.md @@ -0,0 +1,5 @@ +--- +code: 40019 +title: Required SDK plugin not present +summary: The operation needed an optional Ably SDK plugin that was not installed or registered, so it could not be carried out. Some features are provided by plugins that must be added alongside the core SDK. +--- diff --git a/errors/codes/40020.md b/errors/codes/40020.md new file mode 100644 index 00000000..3245d81a --- /dev/null +++ b/errors/codes/40020.md @@ -0,0 +1,5 @@ +--- +code: 40020 +title: Batch request error +summary: A batch request completed with one or more of its items failing. Each failing item carries its own error, reported alongside this one. +--- diff --git a/errors/codes/40021.md b/errors/codes/40021.md new file mode 100644 index 00000000..f5334c2e --- /dev/null +++ b/errors/codes/40021.md @@ -0,0 +1,5 @@ +--- +code: 40021 +title: Feature requires a newer platform version +summary: The requested feature is not available on the platform version in use. It relies on capabilities added in a later version than the one handling the request. +--- diff --git a/errors/codes/40022.md b/errors/codes/40022.md new file mode 100644 index 00000000..2edf052b --- /dev/null +++ b/errors/codes/40022.md @@ -0,0 +1,5 @@ +--- +code: 40022 +title: API Streamer no longer offered +summary: The request targeted API Streamer, which has been shut down and is no longer available. Requests that depend on it can no longer be served. +--- diff --git a/errors/codes/40023.md b/errors/codes/40023.md new file mode 100644 index 00000000..4f93f1a1 --- /dev/null +++ b/errors/codes/40023.md @@ -0,0 +1,5 @@ +--- +code: 40023 +title: Operation requires a newer SDK version +summary: The requested operation is not supported by the Ably SDK version in use, because it relies on a capability available only in a more recent version. +--- diff --git a/errors/codes/40024.md b/errors/codes/40024.md new file mode 100644 index 00000000..0a227fb8 --- /dev/null +++ b/errors/codes/40024.md @@ -0,0 +1,25 @@ +--- +code: 40024 +title: untilAttach history request not served by attach region +summary: A history request using untilAttach was handled by a different Ably region from the one the channel attached in. The attach point is specific to that region, so the request could not be served and no messages were returned. +--- + +## What you should do + +Retrying may not help, as the retry might again be handled by a different region. There is no automatic recovery, so choose the approach that best fits your application: + +- **Retry without untilAttach and remove duplicates yourself.** Request [history](https://ably.com/docs/storage-history/history) without untilAttach and discard any messages you have already received in real time. This keeps a gap-free record but your application has to deduplicate. +- **Retry without untilAttach and accept possible duplicates.** Less work, but some messages may appear both in the history response and among those already received in real time. +- **Treat it as having no retrievable history.** If missing the earlier messages is acceptable, continue with only the messages received in real time. + +Which is best depends on whether duplicate messages or missing history is worse for your use case. + +## Why it happens + +Each Ably region observes and orders recent messages independently, so a given point in one region's order does not necessarily correspond to the same point in another's. + +A history request using untilAttach returns the messages published up to the point at which the channel attached — a point in the order of the region it attached in. If the request is handled by a different region, that region cannot reliably determine which messages fall before that point, so rather than risk returning the wrong set it does not serve the request. + +## What you'll see + +The whole request fails and no messages are returned. The error is reported with code 40024 and HTTP status 400, with the message `Unable to get channel history: cannot serve history from a different site` — "site" here is the internal term for a region. diff --git a/errors/codes/40025.md b/errors/codes/40025.md new file mode 100644 index 00000000..4535718e --- /dev/null +++ b/errors/codes/40025.md @@ -0,0 +1,5 @@ +--- +code: 40025 +title: API no longer supported +summary: A method or feature that Ably no longer supports was called. It has been removed or replaced by a newer API, so the call is rejected instead of being performed. +--- diff --git a/errors/codes/40030.md b/errors/codes/40030.md new file mode 100644 index 00000000..43e9c87b --- /dev/null +++ b/errors/codes/40030.md @@ -0,0 +1,5 @@ +--- +code: 40030 +title: Invalid publish request +summary: A publish request was rejected because it was invalid. +--- diff --git a/errors/codes/40031.md b/errors/codes/40031.md new file mode 100644 index 00000000..6f8838cc --- /dev/null +++ b/errors/codes/40031.md @@ -0,0 +1,5 @@ +--- +code: 40031 +title: Invalid client-specified message id +summary: A publish was rejected because a client-supplied message id was missing, empty, or not in the required format. When several messages are published together, every message must carry a valid id following the expected pattern. +--- diff --git a/errors/codes/40032.md b/errors/codes/40032.md new file mode 100644 index 00000000..de7ec81f --- /dev/null +++ b/errors/codes/40032.md @@ -0,0 +1,5 @@ +--- +code: 40032 +title: Invalid message extras field +summary: A publish was rejected because a message included an extras field that is not permitted, or one whose value was the wrong type. Only recognised extras keys with values of the expected shape are accepted. +--- diff --git a/errors/codes/40033.md b/errors/codes/40033.md new file mode 100644 index 00000000..fcee333b --- /dev/null +++ b/errors/codes/40033.md @@ -0,0 +1,17 @@ +--- +code: 40033 +title: Operation cancelled +summary: An operation was stopped before completing, so its result was discarded. This usually happens when the connection or request that triggered the operation ends first. +--- + +## What you should do + +If you stopped the operation yourself, by closing the connection or abandoning the request before it returned, this is expected and there is nothing to do. Otherwise it did not complete, so if its result mattered, issue it again; it is not retried automatically. + +## Why it happens + +The operation, such as publishing a message, was stopped part-way rather than rejected on its own merits. The usual cause is that the connection or request driving the operation ended first: the connection closed, or the client stopped waiting for the response. Ably stopped processing at that point. + +## What you'll see + +The error is reported with code 40033 and HTTP status 400. The message is `operation cancelled`; the spelling `operation canceled` appears on some paths. diff --git a/errors/codes/40034.md b/errors/codes/40034.md new file mode 100644 index 00000000..039b773e --- /dev/null +++ b/errors/codes/40034.md @@ -0,0 +1,5 @@ +--- +code: 40034 +title: Too many messages in one publish +summary: A publish was rejected because it contained more messages than the maximum permitted count. This is about the number of messages, not their combined size, which is limited separately. +--- diff --git a/errors/codes/40035.md b/errors/codes/40035.md new file mode 100644 index 00000000..a78b64ff --- /dev/null +++ b/errors/codes/40035.md @@ -0,0 +1,5 @@ +--- +code: 40035 +title: Integration message filter regex not RE2-compatible +summary: An integration's message filter could not be applied because its regular expression is not compatible with the RE2 syntax. Backreferences and lookaround are not supported. +--- diff --git a/errors/codes/40099.md b/errors/codes/40099.md new file mode 100644 index 00000000..5d061503 --- /dev/null +++ b/errors/codes/40099.md @@ -0,0 +1,5 @@ +--- +code: 40099 +title: Reserved for testing +summary: This code is reserved for artificial errors produced during testing and does not indicate a genuine fault. It is not expected to appear during normal operation. +--- diff --git a/errors/codes/40100.md b/errors/codes/40100.md new file mode 100644 index 00000000..0b7ff298 --- /dev/null +++ b/errors/codes/40100.md @@ -0,0 +1,5 @@ +--- +code: 40100 +title: Unauthorized +summary: A connection or request was rejected because it was not authorized. +--- diff --git a/errors/codes/40101.md b/errors/codes/40101.md new file mode 100644 index 00000000..bf549de2 --- /dev/null +++ b/errors/codes/40101.md @@ -0,0 +1,5 @@ +--- +code: 40101 +title: Authentication failed +summary: The credentials presented were not accepted — for example an API key secret that did not match, an invalid token, or no credentials supplied at all. +--- diff --git a/errors/codes/40102.md b/errors/codes/40102.md new file mode 100644 index 00000000..be1307d9 --- /dev/null +++ b/errors/codes/40102.md @@ -0,0 +1,5 @@ +--- +code: 40102 +title: Incompatible credentials +summary: The credentials presented were valid but did not match the request — for example the client ID they permit differed from the one in use, or they belonged to a different application than the connection being resumed. +--- diff --git a/errors/codes/40103.md b/errors/codes/40103.md new file mode 100644 index 00000000..cb990f28 --- /dev/null +++ b/errors/codes/40103.md @@ -0,0 +1,5 @@ +--- +code: 40103 +title: Basic authentication used over an insecure connection +summary: Basic authentication was attempted over a connection that was not secured with TLS. Basic authentication sends the API key directly, so it is only permitted over an encrypted transport. +--- diff --git a/errors/codes/40104.md b/errors/codes/40104.md new file mode 100644 index 00000000..8c34e42f --- /dev/null +++ b/errors/codes/40104.md @@ -0,0 +1,5 @@ +--- +code: 40104 +title: Token request timestamp outside permitted window +summary: The timestamp in a token request fell outside the window Ably accepts, so the request was rejected. This usually happens when the clock of the system that generated the token request differs significantly from real time. +--- diff --git a/errors/codes/40105.md b/errors/codes/40105.md new file mode 100644 index 00000000..b8400745 --- /dev/null +++ b/errors/codes/40105.md @@ -0,0 +1,5 @@ +--- +code: 40105 +title: Nonce value replayed +summary: A token request was rejected because its nonce had already been used. Each nonce may be presented only once, so a repeated value is treated as a replayed request. +--- diff --git a/errors/codes/40106.md b/errors/codes/40106.md new file mode 100644 index 00000000..12732e9f --- /dev/null +++ b/errors/codes/40106.md @@ -0,0 +1,5 @@ +--- +code: 40106 +title: No valid authentication method provided +summary: The Ably SDK was given no usable way to authenticate — no API key, token, or token-request mechanism such as authCallback or authUrl was provided. +--- diff --git a/errors/codes/40110.md b/errors/codes/40110.md new file mode 100644 index 00000000..c1edfbbd --- /dev/null +++ b/errors/codes/40110.md @@ -0,0 +1,5 @@ +--- +code: 40110 +title: Account disabled +summary: The request was rejected because the Ably account it belongs to is disabled. While an account is disabled, its applications cannot authenticate or carry traffic. +--- diff --git a/errors/codes/40111.md b/errors/codes/40111.md new file mode 100644 index 00000000..5c67a081 --- /dev/null +++ b/errors/codes/40111.md @@ -0,0 +1,5 @@ +--- +code: 40111 +title: Account connection limit exceeded +summary: A new connection was refused because the account had reached the maximum number of concurrent connections permitted by its limits. +--- diff --git a/errors/codes/40112.md b/errors/codes/40112.md new file mode 100644 index 00000000..efd99d74 --- /dev/null +++ b/errors/codes/40112.md @@ -0,0 +1,5 @@ +--- +code: 40112 +title: Account message limit exceeded +summary: The request was rejected because the account had reached the maximum message volume permitted by its limits. +--- diff --git a/errors/codes/40113.md b/errors/codes/40113.md new file mode 100644 index 00000000..a0e5a267 --- /dev/null +++ b/errors/codes/40113.md @@ -0,0 +1,5 @@ +--- +code: 40113 +title: Account blocked +summary: The request was rejected because the Ably account it belongs to is blocked. +--- diff --git a/errors/codes/40114.md b/errors/codes/40114.md new file mode 100644 index 00000000..67256f06 --- /dev/null +++ b/errors/codes/40114.md @@ -0,0 +1,5 @@ +--- +code: 40114 +title: Account channel limit exceeded +summary: The request was refused because the account had reached the maximum number of concurrent channels permitted by its limits. +--- diff --git a/errors/codes/40115.md b/errors/codes/40115.md new file mode 100644 index 00000000..896c1c11 --- /dev/null +++ b/errors/codes/40115.md @@ -0,0 +1,5 @@ +--- +code: 40115 +title: Account request limit exceeded +summary: The request was refused because the account had reached its permitted limit on API and token requests. +--- diff --git a/errors/codes/40120.md b/errors/codes/40120.md new file mode 100644 index 00000000..47a79c52 --- /dev/null +++ b/errors/codes/40120.md @@ -0,0 +1,5 @@ +--- +code: 40120 +title: Application disabled +summary: The request was rejected because the application it targets is disabled. While an application is disabled, it cannot authenticate or carry traffic. +--- diff --git a/errors/codes/40121.md b/errors/codes/40121.md new file mode 100644 index 00000000..0ceb86ab --- /dev/null +++ b/errors/codes/40121.md @@ -0,0 +1,5 @@ +--- +code: 40121 +title: Token revocation not enabled +summary: A token revocation request was rejected because the revocation capability it requires is not enabled — either token revocation for the application, or revocation by channel for the account. +--- diff --git a/errors/codes/40125.md b/errors/codes/40125.md new file mode 100644 index 00000000..225355a3 --- /dev/null +++ b/errors/codes/40125.md @@ -0,0 +1,5 @@ +--- +code: 40125 +title: Application integration limit exceeded +summary: An integration could not be created because the application had reached the maximum number of integrations permitted by its limits. +--- diff --git a/errors/codes/40126.md b/errors/codes/40126.md new file mode 100644 index 00000000..d5fb623b --- /dev/null +++ b/errors/codes/40126.md @@ -0,0 +1,5 @@ +--- +code: 40126 +title: Application channel rule limit exceeded +summary: A channel rule could not be created because the application had reached the maximum number of channel rules permitted by its limits. +--- diff --git a/errors/codes/40127.md b/errors/codes/40127.md new file mode 100644 index 00000000..fb0ae04d --- /dev/null +++ b/errors/codes/40127.md @@ -0,0 +1,5 @@ +--- +code: 40127 +title: Application API key limit exceeded +summary: A new API key could not be created because the application had reached the maximum number of keys permitted by its limits. +--- diff --git a/errors/codes/40130.md b/errors/codes/40130.md new file mode 100644 index 00000000..a5a6d21e --- /dev/null +++ b/errors/codes/40130.md @@ -0,0 +1,5 @@ +--- +code: 40130 +title: API key not recognised +summary: Authentication failed because the API key presented was not recognised. This usually means the key does not exist or has been removed. +--- diff --git a/errors/codes/40131.md b/errors/codes/40131.md new file mode 100644 index 00000000..4f976633 --- /dev/null +++ b/errors/codes/40131.md @@ -0,0 +1,5 @@ +--- +code: 40131 +title: API key revoked +summary: A connection or request was rejected because the API key it used has been revoked. A revoked key is permanently invalidated and can no longer authenticate. +--- diff --git a/errors/codes/40132.md b/errors/codes/40132.md new file mode 100644 index 00000000..e68d4cbb --- /dev/null +++ b/errors/codes/40132.md @@ -0,0 +1,5 @@ +--- +code: 40132 +title: API key expired +summary: A connection or request was rejected because the API key it used has expired. Keys can be given an expiry time, after which they can no longer authenticate. +--- diff --git a/errors/codes/40133.md b/errors/codes/40133.md new file mode 100644 index 00000000..64fcc4ac --- /dev/null +++ b/errors/codes/40133.md @@ -0,0 +1,5 @@ +--- +code: 40133 +title: Wrong API key for token revocation +summary: A token revocation request was rejected because it was made with a different API key from the one that issued the tokens. Tokens can only be revoked using the key that issued them. +--- diff --git a/errors/codes/40140.md b/errors/codes/40140.md new file mode 100644 index 00000000..f24347b7 --- /dev/null +++ b/errors/codes/40140.md @@ -0,0 +1,5 @@ +--- +code: 40140 +title: Token not accepted +summary: The authentication token was rejected, so the connection or request could not be authenticated. This error prompts the client to obtain a new token and retry. +--- diff --git a/errors/codes/40141.md b/errors/codes/40141.md new file mode 100644 index 00000000..eec0c090 --- /dev/null +++ b/errors/codes/40141.md @@ -0,0 +1,5 @@ +--- +code: 40141 +title: Token revoked +summary: A connection or request was rejected because the authentication token it used has been revoked. A revoked token is invalidated before its normal expiry and can no longer authenticate. +--- diff --git a/errors/codes/40142.md b/errors/codes/40142.md new file mode 100644 index 00000000..3b935198 --- /dev/null +++ b/errors/codes/40142.md @@ -0,0 +1,24 @@ +--- +code: 40142 +title: Token expired +summary: The client's connection or request was rejected because the authentication token had expired. Each token has an expiry time that is set when it is issued. +--- + +## What you should do + +Usually nothing. Token expiry is a normal part of token authentication, and a client configured to renew its own tokens recovers without any intervention: it requests a fresh token and continues. A 40142 that appears briefly and then clears is expected, not a fault. + +You only need to act when the error persists or surfaces to your application. That means renewal isn't happening — a configuration or auth-endpoint problem rather than the expiry itself, covered below. + +## Why it happens + +When 40142 reaches your application instead of being handled silently, the cause is usually one of the following. + +- No renewal mechanism is configured. Without `authUrl` or `authCallback`, the SDK has no way to obtain a replacement when a token expires. [Configure one of these](https://ably.com/docs/auth/token) so renewal happens automatically. +- The auth endpoint is unreachable or slow. If the `authUrl` or `authCallback` fails or times out when the SDK tries to renew, the expired token is all that remains. Check that your token-issuing endpoint is reachable and returns a valid token promptly. +- The token was issued with a very short lifetime. A small `ttl` causes tokens to expire sooner than expected, sometimes before the SDK renews them. Review the `ttl` you request against how long your clients actually need it. +- The client clock is significantly skewed. A token can appear expired immediately after issue if the client's clock differs substantially from real time. Ensure the client clock is reasonably accurate. + +## What you'll see + +The error is reported with code 40142 and HTTP status 401. The message is typically `Token expired`; some server paths report `Access token expired`. diff --git a/errors/codes/40143.md b/errors/codes/40143.md new file mode 100644 index 00000000..5976bef6 --- /dev/null +++ b/errors/codes/40143.md @@ -0,0 +1,19 @@ +--- +code: 40143 +title: Token not recognised +summary: A connection or request was rejected because Ably could not find a record of the token presented. Ably stores a record of certain tokens only while they remain valid, so this often means the token had already expired. +--- + +## What you should do + +Usually nothing, when the client is configured to renew its own tokens. Such a client obtains a replacement when a token is rejected and recovers without intervention, so a 40143 that clears on its own needs no action. In practice it is often indistinguishable from ordinary token expiry. + +Act when it persists or reaches your application. The common cause is a client running on an expired token without renewing it, so confirm an `authUrl` or `authCallback` is [configured](https://ably.com/docs/auth/token) so the client can obtain fresh tokens. + +## Why it happens + +Ably keeps a stored record of some [tokens](https://ably.com/docs/auth/token), and only for as long as they remain valid. This error means a token of that kind was presented but no matching record was found, most often because it had expired and its record had already been removed. That is why such a token is reported as unrecognised rather than expired (40142). + +## What you'll see + +The error is reported with code 40143 and HTTP status 401. The message is `token unrecognised`; some paths include the token identifier, as `Token unrecognised: `. diff --git a/errors/codes/40144.md b/errors/codes/40144.md new file mode 100644 index 00000000..1cf55fca --- /dev/null +++ b/errors/codes/40144.md @@ -0,0 +1,5 @@ +--- +code: 40144 +title: Invalid JWT token +summary: A JWT presented for authentication could not be parsed. The token was not well-formed, so it could not be read as a valid JSON Web Token. +--- diff --git a/errors/codes/40145.md b/errors/codes/40145.md new file mode 100644 index 00000000..10a9564e --- /dev/null +++ b/errors/codes/40145.md @@ -0,0 +1,5 @@ +--- +code: 40145 +title: Invalid Ably token +summary: An Ably token presented for authentication could not be parsed, so its contents could not be read. +--- diff --git a/errors/codes/40150.md b/errors/codes/40150.md new file mode 100644 index 00000000..37f70780 --- /dev/null +++ b/errors/codes/40150.md @@ -0,0 +1,5 @@ +--- +code: 40150 +title: Application connection limit exceeded +summary: A new connection was refused because the application had reached the maximum number of concurrent connections permitted for it. +--- diff --git a/errors/codes/40160.md b/errors/codes/40160.md new file mode 100644 index 00000000..42635e57 --- /dev/null +++ b/errors/codes/40160.md @@ -0,0 +1,23 @@ +--- +code: 40160 +title: Client lacks the required capability +summary: The API key or token used by the client isn't assigned the capability required for the operation — for example publishing, subscribing, or reading history on a channel, or listing channels and connections. +--- + +## What you should do + +If the client authenticates with an API key, assign the [capability](https://ably.com/docs/auth/capabilities) the operation needs to that key. If it authenticates with a [token](https://ably.com/docs/auth/token), update the code that issues tokens so they include that capability. + +A client using an API key picks up the change on its next connection or request, though an existing connection keeps its capabilities until it reconnects. A client using a token keeps the capabilities baked into that token until it is refreshed — reconnecting with the same cached token will not help. If that delay matters, your application needs to trigger the affected clients to reconnect (for an API key) or refresh their token. + +## Why it happens + +Every operation requires a particular capability on the channel or resource it targets, and this error is raised when that capability isn't assigned to the API key or token the client used. Common cases: + +- Publishing or subscribing to a channel without the `publish` or `subscribe` capability. +- Retrieving a channel's history without the `history` capability. +- Entering presence on a channel without the `presence` capability. + +## What you'll see + +The error is reported with code 40160 and HTTP status 401. The wording depends on the operation — the generic form is `operation not permitted with provided capability`, and more specific variants name the capability or resource involved, such as `Unauthorized to publish to channel` or `Listing channels requires the channel-metadata capability`. diff --git a/errors/codes/40161.md b/errors/codes/40161.md new file mode 100644 index 00000000..7ff8a683 --- /dev/null +++ b/errors/codes/40161.md @@ -0,0 +1,5 @@ +--- +code: 40161 +title: Operation requires an identified client +summary: The client had no established client ID, so an operation that requires one was refused. +--- diff --git a/errors/codes/40162.md b/errors/codes/40162.md new file mode 100644 index 00000000..371f2575 --- /dev/null +++ b/errors/codes/40162.md @@ -0,0 +1,5 @@ +--- +code: 40162 +title: Operation requires Basic authentication +summary: The operation was refused because it can only be performed with Basic authentication using an API key, but the client authenticated with a token. Token revocation is one such operation, which must use the key that issued the tokens. +--- diff --git a/errors/codes/40163.md b/errors/codes/40163.md new file mode 100644 index 00000000..11fa5c47 --- /dev/null +++ b/errors/codes/40163.md @@ -0,0 +1,5 @@ +--- +code: 40163 +title: Token revocation not enabled for API key +summary: The operation was refused because the API key used does not have revocable tokens enabled. Revocation applies only to tokens issued by a key with that setting turned on. +--- diff --git a/errors/codes/40164.md b/errors/codes/40164.md new file mode 100644 index 00000000..85e2f6db --- /dev/null +++ b/errors/codes/40164.md @@ -0,0 +1,5 @@ +--- +code: 40164 +title: Token revocation not enabled for API key +summary: The operation was refused because the API key used does not have revocable tokens enabled. Revocation applies only to tokens issued by a key with that setting turned on. +--- diff --git a/errors/codes/40165.md b/errors/codes/40165.md new file mode 100644 index 00000000..e5e4c7ed --- /dev/null +++ b/errors/codes/40165.md @@ -0,0 +1,19 @@ +--- +code: 40165 +title: Channel mode not requested when attaching +summary: Publishing a message, object, or annotation — or entering presence — was rejected because the client did not request the matching channel mode when it attached. The credentials permit the operation; the mode that enables it was not requested. +--- + +## What you should do + +Update the client so that when it attaches to the channel it requests the [channel mode](https://ably.com/docs/channels/options#modes) the operation needs. The error message names the operation that was refused — most commonly, publishing a message needs the `PUBLISH` mode, and entering, updating, or leaving presence needs the `PRESENCE` mode. + +This is not a capability problem, so changing the token or key's capability will not help — the credentials already grant the operation. + +## Why it happens + +This error means the channel mode required to perform the operation was not among those the client requested when it attached — either because it requested a narrower set of modes (for example attaching to subscribe only and then publishing), or because the mode is not one of the [defaults](https://ably.com/docs/channels/options#modes) applied when no modes are specified. The credentials grant the operation; only the attach was missing the mode, so the fix is to request the required mode when attaching rather than to widen the credentials. + +## What you'll see + +The error is reported with code 40165 and HTTP status 401. The message names the operation and the missing mode, for example `Unable to publish a message as client did not request the PUBLISH mode when attaching` or `Unable to enter, update or leave presence as client did not request the PRESENCE mode when attaching`; the object and annotation variants name the `OBJECT_PUBLISH` and `ANNOTATION_PUBLISH` modes. diff --git a/errors/codes/40166.md b/errors/codes/40166.md new file mode 100644 index 00000000..939335bc --- /dev/null +++ b/errors/codes/40166.md @@ -0,0 +1,17 @@ +--- +code: 40166 +title: Unidentified client cannot modify own messages +summary: A message update or delete was rejected because the client is unidentified — it has no clientId — but holds only the "own" form of the relevant capability, message-update-own or message-delete-own, which can never apply to a client that has no messages of its own. +--- + +## What you should do + +Update the client to set a `clientId`, so it connects as an [identified client](https://ably.com/docs/auth/identified-clients) which can own messages that an "own" capability can match; or grant the client the corresponding "any" [capability](https://ably.com/docs/auth/capabilities) — `message-update-any` or `message-delete-any` — which permits acting on messages regardless of author. + +## Why it happens + +The `message-update-own` and `message-delete-own` capabilities only permit a client to change messages it published itself, matched by `clientId`. An unidentified client has no `clientId`, and so no messages of its own, leaving an "own" capability with nothing it is permitted to act on. + +## What you'll see + +The error is reported with code 40166 and HTTP status 401. The message names the operation and the capability required, for example `Unable to update message: anonymous clients require the 'message-update-any' capability (they cannot have own messages, so 'message-update-own' does not apply)`, and the equivalent for delete with `message-delete-any`. diff --git a/errors/codes/40170.md b/errors/codes/40170.md new file mode 100644 index 00000000..3c969a9a --- /dev/null +++ b/errors/codes/40170.md @@ -0,0 +1,5 @@ +--- +code: 40170 +title: Token callback failed +summary: Authentication could not complete because the client's own token-request mechanism, its authCallback or authUrl, returned an error instead of a token or token request. The failure originates in the application's auth logic or endpoint, not within Ably. +--- diff --git a/errors/codes/40171.md b/errors/codes/40171.md new file mode 100644 index 00000000..b99f3f20 --- /dev/null +++ b/errors/codes/40171.md @@ -0,0 +1,5 @@ +--- +code: 40171 +title: Token renewal not configured +summary: The auth token expired and could not be renewed because no renewal mechanism was configured. Without an authCallback, authUrl, or API key, the Ably SDK has no way to obtain a replacement token. +--- diff --git a/errors/codes/40172.md b/errors/codes/40172.md new file mode 100644 index 00000000..618fbe84 --- /dev/null +++ b/errors/codes/40172.md @@ -0,0 +1,5 @@ +--- +code: 40172 +title: Operation requires Token authentication +summary: The operation was refused because it can only be performed with Token authentication, but the client authenticated using Basic authentication. Some operations are available only to clients using a token. +--- diff --git a/errors/codes/40180.md b/errors/codes/40180.md new file mode 100644 index 00000000..71c41302 --- /dev/null +++ b/errors/codes/40180.md @@ -0,0 +1,5 @@ +--- +code: 40180 +title: APNs token authentication required +summary: A location or live-activity notification was not sent because these require the app's APNs credentials to use token-based authentication, but the app is not configured that way. +--- diff --git a/errors/codes/40181.md b/errors/codes/40181.md new file mode 100644 index 00000000..80ba9269 --- /dev/null +++ b/errors/codes/40181.md @@ -0,0 +1,5 @@ +--- +code: 40181 +title: No location token for device +summary: A location push notification was not sent to a device because the device has no location token registered. Location notifications can only be delivered to devices that have supplied one. +--- diff --git a/errors/codes/40300.md b/errors/codes/40300.md new file mode 100644 index 00000000..76856fce --- /dev/null +++ b/errors/codes/40300.md @@ -0,0 +1,5 @@ +--- +code: 40300 +title: Forbidden +summary: The request was refused because it is not permitted. This covers a range of forbidden conditions, such as a disabled account or application, or a caller that lacks permission for the operation. +--- diff --git a/errors/codes/40310.md b/errors/codes/40310.md new file mode 100644 index 00000000..20c93869 --- /dev/null +++ b/errors/codes/40310.md @@ -0,0 +1,5 @@ +--- +code: 40310 +title: Account does not permit TLS connections +summary: The connection was rejected because it used TLS, but the account is configured not to allow TLS connections. +--- diff --git a/errors/codes/40311.md b/errors/codes/40311.md new file mode 100644 index 00000000..337e84fa --- /dev/null +++ b/errors/codes/40311.md @@ -0,0 +1,5 @@ +--- +code: 40311 +title: Application requires a TLS connection +summary: The connection was rejected because it did not use TLS, but the application requires TLS connections. +--- diff --git a/errors/codes/40320.md b/errors/codes/40320.md new file mode 100644 index 00000000..ea55f020 --- /dev/null +++ b/errors/codes/40320.md @@ -0,0 +1,5 @@ +--- +code: 40320 +title: Authentication required +summary: The request was rejected because it carried no authentication credentials. +--- diff --git a/errors/codes/40330.md b/errors/codes/40330.md new file mode 100644 index 00000000..81d6f533 --- /dev/null +++ b/errors/codes/40330.md @@ -0,0 +1,5 @@ +--- +code: 40330 +title: Account not permitted in this cluster or region +summary: The request reached a cluster or region the account is not permitted to use; the accompanying error message gives the specific reason. +--- diff --git a/errors/codes/40331.md b/errors/codes/40331.md new file mode 100644 index 00000000..6171f61a --- /dev/null +++ b/errors/codes/40331.md @@ -0,0 +1,5 @@ +--- +code: 40331 +title: Account not permitted in this cluster +summary: The request reached an Ably cluster the account is not permitted to use. +--- diff --git a/errors/codes/40332.md b/errors/codes/40332.md new file mode 100644 index 00000000..5e83727b --- /dev/null +++ b/errors/codes/40332.md @@ -0,0 +1,5 @@ +--- +code: 40332 +title: Account not permitted in this region +summary: The request reached a region the account is not permitted to use. +--- diff --git a/errors/codes/40400.md b/errors/codes/40400.md new file mode 100644 index 00000000..5afeff27 --- /dev/null +++ b/errors/codes/40400.md @@ -0,0 +1,5 @@ +--- +code: 40400 +title: Resource not found +summary: The requested resource does not exist, often because it has been deleted or the path or identifier used to reference it is incorrect. +--- diff --git a/errors/codes/40500.md b/errors/codes/40500.md new file mode 100644 index 00000000..b4735f74 --- /dev/null +++ b/errors/codes/40500.md @@ -0,0 +1,5 @@ +--- +code: 40500 +title: HTTP method not allowed +summary: The request used an HTTP method that the endpoint does not support. +--- diff --git a/errors/codes/40900.md b/errors/codes/40900.md new file mode 100644 index 00000000..ddfecfc0 --- /dev/null +++ b/errors/codes/40900.md @@ -0,0 +1,5 @@ +--- +code: 40900 +title: Conflict +summary: The request conflicted with the current state of the target, so it could not be completed. +--- diff --git a/errors/codes/41001.md b/errors/codes/41001.md new file mode 100644 index 00000000..3834c192 --- /dev/null +++ b/errors/codes/41001.md @@ -0,0 +1,5 @@ +--- +code: 41001 +title: Push device registration expired +summary: The push notification could not be delivered because the target device's registration is no longer valid. Registrations expire over time, and the device must register again before it can receive notifications. +--- diff --git a/errors/codes/42200.md b/errors/codes/42200.md new file mode 100644 index 00000000..95d29698 --- /dev/null +++ b/errors/codes/42200.md @@ -0,0 +1,5 @@ +--- +code: 42200 +title: Message content could not be processed +summary: A message was rejected because a content check — for example validation or moderation — did not accept it, rather than for a syntax or size problem. +--- diff --git a/errors/codes/42210.md b/errors/codes/42210.md new file mode 100644 index 00000000..b293b4aa --- /dev/null +++ b/errors/codes/42210.md @@ -0,0 +1,5 @@ +--- +code: 42210 +title: Message content rejected +summary: A message was rejected by a content check — such as validation, moderation, or an integration — but the error does not identify which one. +--- diff --git a/errors/codes/42211.md b/errors/codes/42211.md new file mode 100644 index 00000000..e23592de --- /dev/null +++ b/errors/codes/42211.md @@ -0,0 +1,5 @@ +--- +code: 42211 +title: Message rejected by integration +summary: A message was rejected by an integration configured to check messages before they are accepted. +--- diff --git a/errors/codes/42212.md b/errors/codes/42212.md new file mode 100644 index 00000000..c1dc3471 --- /dev/null +++ b/errors/codes/42212.md @@ -0,0 +1,5 @@ +--- +code: 42212 +title: Message rejected by content validation +summary: A message was rejected because it failed a content-validation check. +--- diff --git a/errors/codes/42213.md b/errors/codes/42213.md new file mode 100644 index 00000000..230440d0 --- /dev/null +++ b/errors/codes/42213.md @@ -0,0 +1,5 @@ +--- +code: 42213 +title: Message rejected by moderation +summary: A message was rejected by a content-moderation check configured on the channel. The moderation check inspected the content and flagged it as not permitted. +--- diff --git a/errors/codes/42910.md b/errors/codes/42910.md new file mode 100644 index 00000000..af15868c --- /dev/null +++ b/errors/codes/42910.md @@ -0,0 +1,17 @@ +--- +code: 42910 +title: System rate limit exceeded +summary: An operation was rejected because a system rate limit was exceeded. The limit protects Ably against overload; the operation can be retried. +--- + +## What you should do + +Retry the operation after a short delay, increasing the delay if it is rejected again. This is a protection Ably applies to keep the service healthy under load, not a limit specific to your account, so it is usually brief and clears on its own. If it persists, contact Ably. + +## Why it happens + +Ably applies system-level rate limits to protect the service from overload. While the system is shedding load, individual operations may be rejected until the pressure eases. This is independent of your account's own limits, and the specific protection involved is identified by the metric named in the error message. + +## What you'll see + +The operation is rejected. The error is reported with code 42910 and HTTP status 429, with a message of the form `Rate limit exceeded; request rejected (nonfatal); metric = ...`. diff --git a/errors/codes/42911.md b/errors/codes/42911.md new file mode 100644 index 00000000..86d62e5d --- /dev/null +++ b/errors/codes/42911.md @@ -0,0 +1,19 @@ +--- +code: 42911 +title: Per-connection publish rate exceeded +summary: A message was rejected because the connection published messages faster than the per-connection publish rate limit allows. Publishing far above the limit can escalate to the connection being closed. +--- + +## What you should do + +If the message matters, republish it after a short delay, increasing the delay if it is rejected again. + +To stay within the limit, consider spreading publishing across more connections, since the limit applies to each connection on its own. If a single connection genuinely needs a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Each connection has a maximum rate at which it can publish messages, set by your account's limits. The limit covers everything this connection publishes across all the channels it is attached to, so it is reached when a single connection publishes too quickly overall, regardless of what other connections are doing. + +## What you'll see + +The publish is rejected and the message is not delivered. The connection stays open. The error is reported with code 42911 and HTTP status 429, with a message of the form `Rate limit exceeded; request rejected (nonfatal); metric = connection.inboundRate; ...`. diff --git a/errors/codes/42912.md b/errors/codes/42912.md new file mode 100644 index 00000000..175548fb --- /dev/null +++ b/errors/codes/42912.md @@ -0,0 +1,17 @@ +--- +code: 42912 +title: Channel enumeration already in progress +summary: A request to enumerate the channels active in an app was rejected because another enumeration was already running. Only one channel enumeration can run at a time for an app. +--- + +## What you should do + +Wait for the in-progress enumeration to finish before starting another, since only one can run at a time. If your application makes these requests from more than one place, coordinate them so they do not overlap, or retry after a short delay. + +## Why it happens + +Enumerating the channels active in an app is an expensive operation, so Ably allows only one to run at a time for a given app. A request that arrives while another enumeration is still in progress is rejected rather than queued. + +## What you'll see + +The request is rejected. The error is reported with code 42912 and HTTP status 429, with a message of the form `Channel iteration call already in progress. Only 1 call(s) may be executed concurrently`. "Iteration" here is the internal term for channel enumeration. diff --git a/errors/codes/42913.md b/errors/codes/42913.md new file mode 100644 index 00000000..aefaff18 --- /dev/null +++ b/errors/codes/42913.md @@ -0,0 +1,21 @@ +--- +code: 42913 +title: Per-channel publish rate exceeded +summary: A message was rejected because the rate of messages published to the channel exceeded its configured limit. The limit applies to all publishers to the channel combined, so it can be reached even when no single publisher is publishing quickly. +--- + +## What you should do + +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). + +## Why it happens + +Each channel has a maximum rate at which messages can be published to it, set by your account's limits. This error means messages were published to the channel faster than that limit allows. + +The limit is shared across the whole channel: every message published to it, from any source, counts against one budget. So a channel can exceed its limit even when no individual publisher is publishing quickly, if enough of them publish at once. + +## What you'll see + +The publish is rejected and the message is not delivered. The channel stays attached, and publishing to other channels is unaffected. The error is reported with code 42913 and HTTP status 429, with a message of the form `Rate limit exceeded; request rejected (nonfatal); metric = channel.maxRate; ...`. diff --git a/errors/codes/42914.md b/errors/codes/42914.md new file mode 100644 index 00000000..de3be726 --- /dev/null +++ b/errors/codes/42914.md @@ -0,0 +1,21 @@ +--- +code: 42914 +title: Per-channel publish bandwidth exceeded +summary: A message was rejected because the rate of data published to the channel exceeded its configured bandwidth limit. The limit applies to all publishers to the channel combined, so large messages can reach it even at a low message rate. +--- + +## What you should do + +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, so the data is divided between them rather than concentrated on one. If a channel genuinely needs more bandwidth, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Each channel has a maximum rate at which data can be published to it, set by your account's limits. This error means data was published to the channel faster than that bandwidth limit allows. + +Because the limit measures total data rather than the number of messages, it can be reached by a few large messages as easily as by many small ones. Like the per-channel message-rate limit, it is shared across the whole channel: every message published to it, from any source, counts against one budget. + +## What you'll see + +The publish is rejected and the message is not delivered. The channel stays attached, and publishing to other channels is unaffected. The error is reported with code 42914 and HTTP status 429, with a message of the form `Rate limit exceeded; request rejected (nonfatal); metric = channel.maxBandwidth; ...`. diff --git a/errors/codes/42915.md b/errors/codes/42915.md new file mode 100644 index 00000000..f0a22fac --- /dev/null +++ b/errors/codes/42915.md @@ -0,0 +1,22 @@ +--- +code: 42915 +title: Per-connection outbound message rate exceeded +summary: Messages were not delivered to a connection because the rate of messages being sent to it exceeded its per-connection outbound limit. The client is notified of the gap by a channel continuity loss, which it receives as error 80020. +--- + +## What you should do + +What to do depends on whether your application can tolerate the gap: + +- If it can, no action is needed and the error can be ignored. +- If it cannot, fetch the messages the connection did not receive from [history](https://ably.com/docs/storage-history/history) when it encounters this error. + +To stop hitting the limit, consider spreading subscriptions across more connections, so that each one receives fewer messages. If a connection genuinely needs to receive messages at a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Each connection has a maximum rate at which Ably will deliver messages to it, set by your account's limits. When more messages than that need to be sent to a connection, Ably drops the excess and signals the gap to the client. This code records that the limit was reached; the gap itself is reported to the client as 80020. + +## What you'll see + +This code appears in your app's error statistics, recorded with HTTP status 429 and the metric `connection.outboundRate`. The client itself receives the continuity loss as error 80020. diff --git a/errors/codes/42916.md b/errors/codes/42916.md new file mode 100644 index 00000000..3a9f38cb --- /dev/null +++ b/errors/codes/42916.md @@ -0,0 +1,22 @@ +--- +code: 42916 +title: Per-connection outbound resume rate exceeded +summary: Backlog messages were not delivered to a connection because the rate of delivering them exceeded its per-connection outbound limit. The client is notified of the gap by a channel continuity loss, which it receives as error 80020. +--- + +## What you should do + +What to do depends on whether your application can tolerate the gap: + +- If it can, no action is needed and the error can be ignored. +- If it cannot, fetch the messages the connection did not receive from [history](https://ably.com/docs/storage-history/history) when it encounters this error. + +To stop hitting the limit, consider spreading subscriptions across more connections, so that each one receives fewer messages. If a connection genuinely needs to receive messages at a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +After a connection reattaches to a channel, Ably replays the backlog of messages published while it was away. Each connection has a maximum rate at which Ably will deliver messages to it, set by your account's limits. If the backlog would be delivered faster than that, Ably drops the excess and signals the gap to the client. This code records that the limit was reached while delivering the backlog; the gap itself is reported to the client as 80020. + +## What you'll see + +This code appears in your app's error statistics, recorded with HTTP status 429 and the metric `connection.backlogRate`. The client itself receives the continuity loss as error 80020. diff --git a/errors/codes/42917.md b/errors/codes/42917.md new file mode 100644 index 00000000..2f79e82f --- /dev/null +++ b/errors/codes/42917.md @@ -0,0 +1,19 @@ +--- +code: 42917 +title: Account-wide message publish rate exceeded +summary: A message was rejected because the rate of messages published across the account exceeded the configured account-wide limit. A proportion of messages are rejected to bring the account back within its limit. +--- + +## What you should do + +If the message matters, republish it after a short delay, increasing the delay if it is rejected again. + +This limit applies to your account's total publish rate, so spreading publishing across more connections or channels does not help: every publish in the account counts towards it. If your account consistently needs a higher publish rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Your account has a maximum total rate at which messages can be published across all of its apps, set by your account's limits. When the account exceeds it, Ably rejects a proportion of messages to bring the account back within the limit. Because this applies across the whole account, an individual publish can be rejected even when the connection and channel it used are each well within their own limits. + +## What you'll see + +The publish is rejected and the message is not delivered. The error is reported with code 42917 and HTTP status 429, with a message of the form `Maximum account-wide instantaneous messages rate exceeded; permitted rate = ...; metric = messages.maxRate`. diff --git a/errors/codes/42918.md b/errors/codes/42918.md new file mode 100644 index 00000000..14c26b8f --- /dev/null +++ b/errors/codes/42918.md @@ -0,0 +1,19 @@ +--- +code: 42918 +title: Account-wide API request rate exceeded +summary: A REST API request was rejected because the request rate across the account exceeded the configured account-wide limit. A proportion of requests are rejected to bring the account back within its limit. +--- + +## What you should do + +If the request matters, retry it after a short delay, increasing the delay if it is rejected again. + +This limit applies to your account's total REST API request rate, so making the requests from more clients or API keys does not help: every request in the account counts towards it. If your account consistently needs a higher request rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Your account has a maximum total rate at which REST API requests can be made across all of its apps, set by your account's limits. When the account exceeds it, Ably rejects a proportion of requests to bring the account back within the limit. + +## What you'll see + +The request is rejected. The error is reported with code 42918 and HTTP status 429, with a message of the form `Maximum account-wide instantaneous apiRequests rate exceeded; permitted rate = ...; metric = apiRequests.maxRate`. diff --git a/errors/codes/42920.md b/errors/codes/42920.md new file mode 100644 index 00000000..55d96200 --- /dev/null +++ b/errors/codes/42920.md @@ -0,0 +1,17 @@ +--- +code: 42920 +title: Connection terminated by a fatal rate limit +summary: The connection was closed because a rate limit was exceeded severely enough to be treated as fatal, rather than rejecting individual operations. This is the general form of a fatal rate-limit close; common cases have their own codes. +--- + +## What you should do + +The error message names the metric for the limit involved. Reduce the rate of the operation it identifies before reconnecting, otherwise the connection will be closed again if the behaviour continues. + +## Why it happens + +Most rate limits reject individual operations without closing the connection. When a breach is severe or sustained enough, Ably closes the connection instead. This code is the general form of such a fatal close; specific cases have their own codes, such as 42921 for per-connection publishing and 42924 for protocol messages. + +## What you'll see + +The connection is closed. The error is reported with code 42920 and HTTP status 429. diff --git a/errors/codes/42921.md b/errors/codes/42921.md new file mode 100644 index 00000000..653b43b4 --- /dev/null +++ b/errors/codes/42921.md @@ -0,0 +1,17 @@ +--- +code: 42921 +title: Connection terminated for far exceeding the per-connection publish rate +summary: The connection was closed because it published far above the per-connection publish rate limit — around 20 times the permitted rate. Breaches below that reject individual messages without closing the connection. +--- + +## What you should do + +The connection can be re-established, but it will be closed again if it keeps publishing far above the limit. When individual publishes start being rejected, back off instead of retrying at full rate. If you need a higher overall publish rate, spread publishing across more connections, or [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +A connection that exceeds its per-connection publish rate limit normally just has the offending messages rejected with error 42911. If it keeps publishing far above the limit — around 20 times the permitted rate — Ably closes the connection instead, to protect the service from a runaway publisher. + +## What you'll see + +The connection is closed; unlike 42911, this is fatal. The error is reported with code 42921 and HTTP status 429, with a message of the form `Connection terminated; per-connection publish Rate limit exceeded; ...`. diff --git a/errors/codes/42922.md b/errors/codes/42922.md new file mode 100644 index 00000000..521106dc --- /dev/null +++ b/errors/codes/42922.md @@ -0,0 +1,17 @@ +--- +code: 42922 +title: Too many requests +summary: A request was blocked because too many requests were received in a short period, triggering Ably's flood protection. This protects the service against excessive or abusive traffic, separately from your account's own rate limits. +--- + +## What you should do + +Back off and retry rather than repeating the request immediately, as short-lived bursts clear on their own. If your application is blocked persistently during legitimate use, contact Ably. + +## Why it happens + +Ably protects its service from excessive or abusive traffic by limiting requests at its network edge, before they reach the rest of the platform. When too many requests arrive in a short period, the excess is blocked until the rate subsides. This protection is separate from your account's configured rate limits. + +## What you'll see + +The request is rejected with HTTP status 429. Because it is blocked at the network edge rather than by the platform itself, the response carries the error in the headers `x-ably-errorcode: 42922` and `x-ably-errormessage: rate limit exceeded; too many requests`. diff --git a/errors/codes/42923.md b/errors/codes/42923.md new file mode 100644 index 00000000..22daf5fd --- /dev/null +++ b/errors/codes/42923.md @@ -0,0 +1,22 @@ +--- +code: 42923 +title: Integration target responded with rate limit error +summary: An integration target such as an HTTP endpoint, serverless function, or message queue returned a rate-limit response when Ably invoked it. The limit is the target's own, not one of Ably's. +--- + +## What you should do + +This is your integration target's own rate limit rather than an Ably one: the target could not keep up with the rate at which Ably was invoking it. You can address it from either side: + +- **Let the target accept more.** Increase its capacity, or raise its own rate limit. +- **Invoke the target less often.** Narrow the integration's channel filter so it covers fewer channels. The filter is configured on the integration, for both [webhooks](https://ably.com/docs/platform/integrations/webhooks#filter) and [streaming integrations](https://ably.com/docs/platform/integrations/streaming#filter). + +Ably retries failed invocations, so short bursts may clear on their own. + +## Why it happens + +Whenever a message matches one of your integrations, Ably invokes the target you configured, such as a webhook endpoint, serverless function, or message queue. If that target responds with its own rate-limit error, Ably reports it under this code. It means the target is accepting invocations more slowly than Ably is making them, not that any Ably limit was reached. + +## What you'll see + +The error is reported with code 42923 and HTTP status 429. The exact message depends on the integration type, for example `HTTP endpoint returned rate limit error` for a webhook or `lambda returned rate limit error` for an AWS Lambda function. diff --git a/errors/codes/42924.md b/errors/codes/42924.md new file mode 100644 index 00000000..a6357699 --- /dev/null +++ b/errors/codes/42924.md @@ -0,0 +1,17 @@ +--- +code: 42924 +title: Per-connection protocol message rate exceeded +summary: A connection was terminated because the rate of inbound protocol messages from the connection exceeded its configured per-connection limit. Protocol messages include message publishes, presence updates, attach/detach requests, and other client-initiated actions on the connection. +--- + +## What you should do + +The threshold is high (around 2000 protocol messages per second), well above what the official Ably SDKs send, so reaching it usually indicates a client that is not using an official SDK and is sending far more protocol messages than intended. Look for a loop or bug in that client, such as repeatedly attaching and detaching a channel or publishing in a tight loop. The connection can be re-established, but it will be closed again if the behaviour continues. + +## Why it happens + +A connection may send only a limited number of protocol messages per second, around 2000, covering every client-initiated action on it and not just published messages. This is a safeguard against a misbehaving or runaway client. When a connection exceeds it, Ably closes the connection rather than rejecting individual messages. + +## What you'll see + +The connection is closed. The error is reported with code 42924 and HTTP status 429, with a message of the form `Connection terminated for abuse (inbound protocol message rate exceeded 2000/s)`. diff --git a/errors/codes/42925.md b/errors/codes/42925.md new file mode 100644 index 00000000..0ba207c9 --- /dev/null +++ b/errors/codes/42925.md @@ -0,0 +1,19 @@ +--- +code: 42925 +title: Account-wide integration invocation rate exceeded +summary: An integration invocation was dropped because the invocation rate across the account exceeded the configured account-wide limit. A proportion of invocations are dropped to bring the account back within its limit. +--- + +## What you should do + +There is nothing to retry from your application: the messages that trigger your integrations are published as normal, but a proportion of the invocations they would cause are dropped while the account is over its limit. + +This limit counts every integration invocation across your whole account. A high rate often comes from an integration whose channel filter matches a large number of channels, so its invocations add up across all of them. If your application does not need a filter that broad, narrowing it reduces how often the integration is invoked. The filter is configured on the integration itself, for both [webhooks](https://ably.com/docs/platform/integrations/webhooks#filter) and [streaming integrations](https://ably.com/docs/platform/integrations/streaming#filter). If your account legitimately needs a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Your account has a maximum total rate at which integrations can be invoked across all of its apps, set by your account's limits. This covers all of your integrations together, such as webhooks, serverless functions, and message queues. When the account exceeds the limit, Ably drops a proportion of invocations to bring it back within the limit. The messages that triggered them are published as normal. + +## What you'll see + +The triggering messages are published normally; only the integration invocations are affected. The error is reported with code 42925 and HTTP status 429, with a message of the form `Maximum account-wide instantaneous reactor.webhook rate exceeded; permitted rate = ...; metric = reactor.webhook.maxRate`. The metric names the integration type, and "reactor" is the internal term for integrations. diff --git a/errors/codes/42926.md b/errors/codes/42926.md new file mode 100644 index 00000000..30d2fdae --- /dev/null +++ b/errors/codes/42926.md @@ -0,0 +1,19 @@ +--- +code: 42926 +title: Account-wide push notification rate exceeded +summary: A push notification was dropped because the publish rate of push notifications across the account exceeded the configured account-wide limit. A proportion of push notifications are dropped to bring the account back within its limit. +--- + +## What you should do + +There is nothing to retry from your application: the messages are published as normal, but a proportion of the push notifications they trigger are dropped while the account is over its limit. + +This limit applies to your account's total push notification rate, counting every push across all of its apps. If your account legitimately needs a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Your account has a maximum total rate at which push notifications can be sent across all of its apps, set by your account's limits. When the account exceeds it, Ably drops a proportion of push notifications to bring the account back within the limit. Message publishing is unaffected. + +## What you'll see + +Affected push notifications are not delivered, while message publishing continues as normal. The error is reported with code 42926 and HTTP status 429, with a message of the form `Maximum account-wide instantaneous pushRequests rate exceeded; permitted rate = ...; metric = pushRequests.maxRate`. diff --git a/errors/codes/50000.md b/errors/codes/50000.md new file mode 100644 index 00000000..b3bc37db --- /dev/null +++ b/errors/codes/50000.md @@ -0,0 +1,5 @@ +--- +code: 50000 +title: Internal server error +summary: The request could not be completed because of an unexpected error inside the Ably platform. +--- diff --git a/errors/codes/50001.md b/errors/codes/50001.md new file mode 100644 index 00000000..5f80e142 --- /dev/null +++ b/errors/codes/50001.md @@ -0,0 +1,5 @@ +--- +code: 50001 +title: Internal channel error +summary: An operation on a channel failed because of an unexpected error inside the Ably platform. +--- diff --git a/errors/codes/50002.md b/errors/codes/50002.md new file mode 100644 index 00000000..2710c1c4 --- /dev/null +++ b/errors/codes/50002.md @@ -0,0 +1,5 @@ +--- +code: 50002 +title: Internal connection error +summary: An operation on a connection failed because of an unexpected error inside the Ably platform. +--- diff --git a/errors/codes/50003.md b/errors/codes/50003.md new file mode 100644 index 00000000..d1ade2b8 --- /dev/null +++ b/errors/codes/50003.md @@ -0,0 +1,5 @@ +--- +code: 50003 +title: Request timed out server-side +summary: The Ably platform did not finish handling the request within the time allowed, so the request was abandoned. This usually reflects a transient server-side delay rather than a problem with the request. +--- diff --git a/errors/codes/50004.md b/errors/codes/50004.md new file mode 100644 index 00000000..35dc920b --- /dev/null +++ b/errors/codes/50004.md @@ -0,0 +1,5 @@ +--- +code: 50004 +title: Server temporarily busy +summary: The request was rejected because the server handling it could not accept it at that moment. The condition is transient. +--- diff --git a/errors/codes/50005.md b/errors/codes/50005.md new file mode 100644 index 00000000..6b101ce6 --- /dev/null +++ b/errors/codes/50005.md @@ -0,0 +1,5 @@ +--- +code: 50005 +title: Service temporarily unavailable +summary: The request could not be served because the service was temporarily in a locked-down state, during which requests are not accepted. The condition is temporary and expected to clear. +--- diff --git a/errors/codes/50006.md b/errors/codes/50006.md new file mode 100644 index 00000000..bee7085c --- /dev/null +++ b/errors/codes/50006.md @@ -0,0 +1,5 @@ +--- +code: 50006 +title: Connection superseded by a newer connection +summary: The connection's state was taken over by a more recent connection, typically a reconnection or resume. This only arises on a deprecated Ably protocol version; upgrade to a recent Ably SDK release to avoid it. +--- diff --git a/errors/codes/50010.md b/errors/codes/50010.md new file mode 100644 index 00000000..38910d27 --- /dev/null +++ b/errors/codes/50010.md @@ -0,0 +1,5 @@ +--- +code: 50010 +title: Edge proxy internal error +summary: Ably's edge proxy service encountered an unknown internal error while handling the request and could not complete it. +--- diff --git a/errors/codes/50210.md b/errors/codes/50210.md new file mode 100644 index 00000000..cae48b64 --- /dev/null +++ b/errors/codes/50210.md @@ -0,0 +1,5 @@ +--- +code: 50210 +title: Invalid response from the platform +summary: Ably's edge proxy service received an invalid response from the Ably platform behind it and could not complete the request. The condition is usually transient. +--- diff --git a/errors/codes/50310.md b/errors/codes/50310.md new file mode 100644 index 00000000..cf4a3cac --- /dev/null +++ b/errors/codes/50310.md @@ -0,0 +1,5 @@ +--- +code: 50310 +title: Platform temporarily unavailable +summary: Ably's edge proxy service received a service-unavailable response from the Ably platform behind it and could not complete the request. The condition is usually transient. +--- diff --git a/errors/codes/50320.md b/errors/codes/50320.md new file mode 100644 index 00000000..2ef1f8ae --- /dev/null +++ b/errors/codes/50320.md @@ -0,0 +1,5 @@ +--- +code: 50320 +title: Traffic temporarily redirected +summary: Ably's Active Traffic Management temporarily redirected traffic away from its normal path, and the request was affected while that redirection was in place. The condition is temporary. +--- diff --git a/errors/codes/50330.md b/errors/codes/50330.md new file mode 100644 index 00000000..f90af9cb --- /dev/null +++ b/errors/codes/50330.md @@ -0,0 +1,5 @@ +--- +code: 50330 +title: Request reached the wrong cluster +summary: The request arrived at a cluster that was not the one meant to handle it and should be retried. This can happen briefly during DNS changes while traffic is being migrated between clusters. +--- diff --git a/errors/codes/50410.md b/errors/codes/50410.md new file mode 100644 index 00000000..1d06b8c3 --- /dev/null +++ b/errors/codes/50410.md @@ -0,0 +1,5 @@ +--- +code: 50410 +title: Edge proxy timed out waiting for platform +summary: Ably's edge proxy service did not receive a response from the Ably platform behind it within the time allowed, so the request was abandoned. The condition is usually transient. +--- diff --git a/errors/codes/70000.md b/errors/codes/70000.md new file mode 100644 index 00000000..8b5b74b1 --- /dev/null +++ b/errors/codes/70000.md @@ -0,0 +1,5 @@ +--- +code: 70000 +title: Integration operation failed +summary: An integration could not complete an operation against its configured target. +--- diff --git a/errors/codes/70001.md b/errors/codes/70001.md new file mode 100644 index 00000000..a4260b9c --- /dev/null +++ b/errors/codes/70001.md @@ -0,0 +1,19 @@ +--- +code: 70001 +title: Integration invocation failed +summary: An integration could not invoke its configured target: Ably either could not reach the target, or reached it but could not deliver the data. The specific failure depends on the integration type and is named in the error message. +--- + +## What you should do + +Start with the underlying error in the message: it names the specific failure for the integration type. For a webhook or other HTTP target, that is typically a refused or reset connection, a timeout, a DNS lookup failure, or a TLS certificate the connection won't accept. For a queue or streaming target such as Kafka, Kinesis, or SQS, it is typically a failure to connect to the service or to deliver the message. Use it to check that the configured target is reachable, correctly addressed, and accepting data from Ably. + +Ably retries failed invocations with a backoff, so a brief outage recovers on its own. Persistent failures mean the target stays unavailable and data is not being delivered. + +## Why it happens + +When a message matches one of your [integrations](https://ably.com/docs/integrations), Ably invokes the target you configured. This error means that invocation failed: Ably could not connect to the target, or connected but could not deliver to it. Where the target does respond but rejects the request, the error is reported as 70002 instead. + +## What you'll see + +The error is reported with code 70001. The message names the operation that failed and includes the underlying error, in a form that depends on the integration type: for an HTTP target, `Webhook operation failed (post operation failed); err = ` or `POST failed: `, carrying HTTP status 503; for other integration types, a target-specific message such as `Kafka client was unable to send the message: `. diff --git a/errors/codes/70002.md b/errors/codes/70002.md new file mode 100644 index 00000000..946bd222 --- /dev/null +++ b/errors/codes/70002.md @@ -0,0 +1,19 @@ +--- +code: 70002 +title: Integration target returned an error status +summary: An integration invoked an HTTP target, such as a webhook, which replied with an HTTP status outside the successful 2xx range. Ably treats any non-2xx response as a failed delivery. +--- + +## What you should do + +Determine the HTTP status code the target returned from the error message, and check the target's own logs, since it received the request and chose to reject it. A 4xx status points to the request being treated as malformed or unauthorised, so check any authentication, required headers, or payload shape the target enforces. A 5xx status points to a fault in the target itself. + +To have a delivery counted as successful, the target must respond with a 2xx status. Ably retries server errors with a backoff, so a transient 5xx recovers on its own; repeated client errors will keep failing until the target accepts the request. + +## Why it happens + +When a message matches one of your [integrations](https://ably.com/docs/integrations), Ably invokes the target you configured. For an HTTP target such as a webhook, only a 2xx response counts as success; this error means the target replied with something else, commonly a 4xx because it rejected the request or a 5xx because it failed to process it. Where no response is received at all, the error is reported as 70001 instead. + +## What you'll see + +The error is reported with code 70002, and the target's own status code is included in the message, which takes the form `Webhook operation failed (post operation returned unexpected code); statusCode = ` or `POST returned HTTP status `. diff --git a/errors/codes/70003.md b/errors/codes/70003.md new file mode 100644 index 00000000..58768c4f --- /dev/null +++ b/errors/codes/70003.md @@ -0,0 +1,5 @@ +--- +code: 70003 +title: Too many concurrent integration requests +summary: An integration was not invoked because the number of requests already in flight for it had reached the permitted maximum. This usually happens when a target responds more slowly than the rate at which matching messages arrive. +--- diff --git a/errors/codes/70004.md b/errors/codes/70004.md new file mode 100644 index 00000000..ae0e7d00 --- /dev/null +++ b/errors/codes/70004.md @@ -0,0 +1,5 @@ +--- +code: 70004 +title: Integration refused to process a message +summary: An integration reached its configured target, but the target did not accept the message because its contents were invalid or unsupported for that target type. +--- diff --git a/errors/codes/70005.md b/errors/codes/70005.md new file mode 100644 index 00000000..dea53f3f --- /dev/null +++ b/errors/codes/70005.md @@ -0,0 +1,5 @@ +--- +code: 70005 +title: AMQP message delivery timed out +summary: A message could not be delivered to an Ably message queue integration because the delivery did not complete within the allowed time. This usually points to the queue being unavailable or slow to accept the message. +--- diff --git a/errors/codes/70006.md b/errors/codes/70006.md new file mode 100644 index 00000000..da37f193 --- /dev/null +++ b/errors/codes/70006.md @@ -0,0 +1,5 @@ +--- +code: 70006 +title: AMQP message queue busy +summary: A message could not be delivered to an Ably message queue integration because the message queue was too busy to accept it. This is usually transient and eases as load falls. +--- diff --git a/errors/codes/71000.md b/errors/codes/71000.md new file mode 100644 index 00000000..0258c217 --- /dev/null +++ b/errors/codes/71000.md @@ -0,0 +1,5 @@ +--- +code: 71000 +title: Exchange error +summary: A request involving Ably Exchange failed for a reason that could not be attributed to a more specific cause. Exchange lets a publisher expose channels as a product that subscribers can receive. +--- diff --git a/errors/codes/71001.md b/errors/codes/71001.md new file mode 100644 index 00000000..852398a9 --- /dev/null +++ b/errors/codes/71001.md @@ -0,0 +1,5 @@ +--- +code: 71001 +title: Forced re-attachment after permissions change +summary: An Exchange channel was detached and re-attached because the permissions governing access to it changed, so the existing attachment no longer reflected what the client was entitled to receive. +--- diff --git a/errors/codes/71100.md b/errors/codes/71100.md new file mode 100644 index 00000000..c87d29c7 --- /dev/null +++ b/errors/codes/71100.md @@ -0,0 +1,5 @@ +--- +code: 71100 +title: Exchange publisher error +summary: A request involving an Exchange publisher failed for a reason that could not be attributed to a more specific cause. A publisher is the party that exposes channels as an Exchange product. +--- diff --git a/errors/codes/71101.md b/errors/codes/71101.md new file mode 100644 index 00000000..90d9fc2a --- /dev/null +++ b/errors/codes/71101.md @@ -0,0 +1,5 @@ +--- +code: 71101 +title: Publisher not found +summary: The Exchange request referred to a publisher that does not exist. This usually means the publisher identifier was incorrect or the publisher is no longer available. +--- diff --git a/errors/codes/71102.md b/errors/codes/71102.md new file mode 100644 index 00000000..546cfb59 --- /dev/null +++ b/errors/codes/71102.md @@ -0,0 +1,5 @@ +--- +code: 71102 +title: Publisher not enabled for Exchange +summary: The request referred to a party that exists but has not been enabled as an Exchange publisher, so it cannot expose channels as a product for subscribers to receive. +--- diff --git a/errors/codes/71200.md b/errors/codes/71200.md new file mode 100644 index 00000000..58e5f4f0 --- /dev/null +++ b/errors/codes/71200.md @@ -0,0 +1,5 @@ +--- +code: 71200 +title: Exchange product error +summary: A request involving an Exchange product failed for a reason that could not be attributed to a more specific cause. A product is the set of channels a publisher exposes for subscribers to receive. +--- diff --git a/errors/codes/71201.md b/errors/codes/71201.md new file mode 100644 index 00000000..65e42321 --- /dev/null +++ b/errors/codes/71201.md @@ -0,0 +1,5 @@ +--- +code: 71201 +title: Product not found +summary: The Exchange request referred to a product that does not exist. This usually means the product identifier was incorrect or the product is no longer available from the publisher. +--- diff --git a/errors/codes/71202.md b/errors/codes/71202.md new file mode 100644 index 00000000..b82e0844 --- /dev/null +++ b/errors/codes/71202.md @@ -0,0 +1,5 @@ +--- +code: 71202 +title: Product disabled +summary: The Exchange product exists but has been disabled by the publisher, so its channels are not currently available to subscribers. +--- diff --git a/errors/codes/71203.md b/errors/codes/71203.md new file mode 100644 index 00000000..b18c6b35 --- /dev/null +++ b/errors/codes/71203.md @@ -0,0 +1,5 @@ +--- +code: 71203 +title: Channel not part of product +summary: The requested channel is not one of the channels that the Exchange product exposes, so it cannot be received through that product. +--- diff --git a/errors/codes/71204.md b/errors/codes/71204.md new file mode 100644 index 00000000..9d3b98ac --- /dev/null +++ b/errors/codes/71204.md @@ -0,0 +1,5 @@ +--- +code: 71204 +title: Forced re-attachment after product remapping +summary: An Exchange channel was detached and re-attached because the product was remapped to a different channel namespace, so the existing attachment no longer pointed at the channel the product now exposes. +--- diff --git a/errors/codes/71300.md b/errors/codes/71300.md new file mode 100644 index 00000000..a4e24df6 --- /dev/null +++ b/errors/codes/71300.md @@ -0,0 +1,5 @@ +--- +code: 71300 +title: Exchange subscription error +summary: A request involving an Exchange subscription failed for a reason that could not be attributed to a more specific cause. A subscription is what grants a subscriber access to a publisher's product. +--- diff --git a/errors/codes/71301.md b/errors/codes/71301.md new file mode 100644 index 00000000..cfb36272 --- /dev/null +++ b/errors/codes/71301.md @@ -0,0 +1,5 @@ +--- +code: 71301 +title: Subscription disabled +summary: The subscription to the Exchange product exists but has been disabled, so its channels are not currently available to the subscriber. +--- diff --git a/errors/codes/71302.md b/errors/codes/71302.md new file mode 100644 index 00000000..a67395fb --- /dev/null +++ b/errors/codes/71302.md @@ -0,0 +1,5 @@ +--- +code: 71302 +title: No subscription to product +summary: The requester attempted to receive an Exchange product they are not subscribed to. Access to a product's channels requires an active subscription to that product. +--- diff --git a/errors/codes/71303.md b/errors/codes/71303.md new file mode 100644 index 00000000..4ad1f1ca --- /dev/null +++ b/errors/codes/71303.md @@ -0,0 +1,5 @@ +--- +code: 71303 +title: Channel outside subscription filter +summary: The requested channel does not match the channel filter defined for the subscription to the Exchange product, so it falls outside the set of channels that subscription grants access to. +--- diff --git a/errors/codes/72000.md b/errors/codes/72000.md new file mode 100644 index 00000000..502bdcf9 --- /dev/null +++ b/errors/codes/72000.md @@ -0,0 +1,5 @@ +--- +code: 72000 +title: LiveSync connector operation failed +summary: A LiveSync database connector hit an unexpected error while running, so the operation it was attempting did not complete. +--- diff --git a/errors/codes/72001.md b/errors/codes/72001.md new file mode 100644 index 00000000..c8a980a4 --- /dev/null +++ b/errors/codes/72001.md @@ -0,0 +1,5 @@ +--- +code: 72001 +title: LiveSync connector could not publish +summary: A LiveSync database connector read a change from the database but could not publish the resulting message to Ably. The change may not have reached the channel it was destined for. +--- diff --git a/errors/codes/72002.md b/errors/codes/72002.md new file mode 100644 index 00000000..2017e61b --- /dev/null +++ b/errors/codes/72002.md @@ -0,0 +1,23 @@ +--- +code: 72002 +title: LiveSync database connector is unhealthy +summary: A LiveSync database connector could not read from the table or collection it is configured to use, because that table or collection is missing, renamed, or no longer accessible to the connector. +--- + +## What you should do + +The connector runs a periodic health check on the database it reads from, and this error means that check failed. What to confirm depends on which connector you are using. + +For a [Postgres connector](https://ably.com/docs/livesync/postgres), confirm the outbox and nodes tables it uses still exist, are named correctly in the connector configuration, and that the connector's database user still has access to them. + +For a [MongoDB connector](https://ably.com/docs/livesync/mongodb), confirm the watched collection still exists, is named correctly in the connector configuration, and that the connector's database user still has the `find` and `changeStream` permissions on it. + +Once the underlying problem is resolved, the connector recovers on its next health check; no manual restart is needed. + +## Why it happens + +A [LiveSync](https://ably.com/docs/products/livesync) database connector continuously reads changes from a table or collection and publishes them to Ably. This error means the connector could not open or query that table or collection, because it is missing, has been renamed, or its permissions have changed. A database the connector cannot connect to at all is reported as 72003 instead, and an individual change that cannot be mapped to a channel as 72004. + +## What you'll see + +The error is reported with code 72002 and HTTP status 500. The message names the affected table and takes the form `unable to connect to table: : table is unhealthy: `, or `unable to connect to tables: ` when the connector cannot open its tables at startup. diff --git a/errors/codes/72003.md b/errors/codes/72003.md new file mode 100644 index 00000000..91e78fa7 --- /dev/null +++ b/errors/codes/72003.md @@ -0,0 +1,5 @@ +--- +code: 72003 +title: LiveSync connector cannot reach the database +summary: A LiveSync database connector could not establish a connection to the database it reads from. This often points to the database being unreachable, or to incorrect connection details or credentials in the connector configuration. +--- diff --git a/errors/codes/72004.md b/errors/codes/72004.md new file mode 100644 index 00000000..6137a234 --- /dev/null +++ b/errors/codes/72004.md @@ -0,0 +1,5 @@ +--- +code: 72004 +title: LiveSync connector could not identify target channel +summary: A LiveSync database connector read a change but could not determine which channel to publish it to, because the change had no _ablyChannel field. Only changes that specify a channel can be published. +--- diff --git a/errors/codes/72005.md b/errors/codes/72005.md new file mode 100644 index 00000000..16685972 --- /dev/null +++ b/errors/codes/72005.md @@ -0,0 +1,5 @@ +--- +code: 72005 +title: LiveSync MongoDB pipeline is invalid +summary: A LiveSync MongoDB connector could not start because the aggregation pipeline in its configuration was not accepted by MongoDB, for example because it references a stage or field that is not allowed. +--- diff --git a/errors/codes/72006.md b/errors/codes/72006.md new file mode 100644 index 00000000..a68b652b --- /dev/null +++ b/errors/codes/72006.md @@ -0,0 +1,5 @@ +--- +code: 72006 +title: LiveSync connector failed to resume +summary: A LiveSync MongoDB connector could not resume its change stream from where it left off. It stores a resume token to continue after a restart, and this arises when that stored position cannot be read back. +--- diff --git a/errors/codes/72007.md b/errors/codes/72007.md new file mode 100644 index 00000000..d56f0e24 --- /dev/null +++ b/errors/codes/72007.md @@ -0,0 +1,5 @@ +--- +code: 72007 +title: LiveSync connector failed to track its position +summary: A LiveSync MongoDB connector could not save its change-stream resume token to the database. The token records how far the connector has read, so that it can continue from the same point after a restart. +--- diff --git a/errors/codes/72008.md b/errors/codes/72008.md new file mode 100644 index 00000000..a0e7586b --- /dev/null +++ b/errors/codes/72008.md @@ -0,0 +1,5 @@ +--- +code: 72008 +title: LiveSync connector failed to read history +summary: A LiveSync MongoDB connector's change stream could no longer continue from its last recorded position, because the history it needed was no longer available in the database. Changes made in the intervening period may have been missed. +--- diff --git a/errors/codes/80000.md b/errors/codes/80000.md new file mode 100644 index 00000000..b503ac92 --- /dev/null +++ b/errors/codes/80000.md @@ -0,0 +1,5 @@ +--- +code: 80000 +title: Connection failed +summary: The connection moved to the failed state, a terminal condition from which the SDK does not automatically reconnect. It differs from a temporary disconnection, where the SDK keeps retrying to restore the connection on its own. +--- diff --git a/errors/codes/80001.md b/errors/codes/80001.md new file mode 100644 index 00000000..9ab22ae1 --- /dev/null +++ b/errors/codes/80001.md @@ -0,0 +1,5 @@ +--- +code: 80001 +title: Connection failed as no usable transport available +summary: No usable transport could be established to carry the connection, for example when the network blocks both WebSocket and the HTTP-based fallbacks. +--- diff --git a/errors/codes/80002.md b/errors/codes/80002.md new file mode 100644 index 00000000..247cdd89 --- /dev/null +++ b/errors/codes/80002.md @@ -0,0 +1,5 @@ +--- +code: 80002 +title: Connection suspended +summary: The connection moved to the suspended state after being disconnected for an extended period. A connection becomes suspended once it has been disconnected for around two minutes, beyond which its state can no longer be recovered. +--- diff --git a/errors/codes/80003.md b/errors/codes/80003.md new file mode 100644 index 00000000..c3965ddf --- /dev/null +++ b/errors/codes/80003.md @@ -0,0 +1,19 @@ +--- +code: 80003 +title: Connection disconnected +summary: The connection to Ably was dropped. This is a normal, usually brief interruption, such as a network change or Ably cycling the connection, rather than a deliberate close, and the connection is expected to be re-established. +--- + +## What you should do + +Usually nothing. Disconnection is a routine part of maintaining a realtime connection, and the Ably SDK handles it for you: it moves the connection to the `disconnected` state, retries automatically, and returns to `connected`, resuming where it left off. A 80003 that clears on its own is expected, not a fault. + +Act only if the connection does not return, or disconnects often enough to disrupt your application. Frequent disconnections usually point to the network path between the client and Ably, such as the client's own connectivity, or a proxy, firewall, or load balancer that drops long-lived connections; check there first. + +## Why it happens + +The transport carrying the connection was lost, commonly a network change or interruption on the client side, or Ably closing the transport so the client reconnects. It is not an authentication failure or a deliberate close by your application, both of which are reported with their own codes. While disconnected, the SDK keeps any messages you publish and sends them once it reconnects. + +## What you'll see + +This code is not delivered as a server response in the usual case; it is the reason attached to the connection's `disconnected` state. The connection enters the `disconnected` state, carrying reason code 80003 with the message `Connection disconnected`, and returns to `connected` once the transport is re-established. diff --git a/errors/codes/80004.md b/errors/codes/80004.md new file mode 100644 index 00000000..e2f8f357 --- /dev/null +++ b/errors/codes/80004.md @@ -0,0 +1,5 @@ +--- +code: 80004 +title: Connection already established +summary: A request to open a connection was made while a connection was already active. +--- diff --git a/errors/codes/80005.md b/errors/codes/80005.md new file mode 100644 index 00000000..2816e160 --- /dev/null +++ b/errors/codes/80005.md @@ -0,0 +1,5 @@ +--- +code: 80005 +title: Connection no longer available +summary: A request referenced an existing connection, but the server no longer held its state — typically because the connection had been idle and was released after a period without activity. The client is signalled to reconnect and continue on a fresh connection. +--- diff --git a/errors/codes/80006.md b/errors/codes/80006.md new file mode 100644 index 00000000..25e0e1ad --- /dev/null +++ b/errors/codes/80006.md @@ -0,0 +1,5 @@ +--- +code: 80006 +title: Connection continuity not guaranteed as messages expired +summary: A connection was resumed after the messages needed to bridge the gap had expired. The connection continues, but any messages published during the disconnection may not be redelivered, so continuity across it is not guaranteed. +--- diff --git a/errors/codes/80007.md b/errors/codes/80007.md new file mode 100644 index 00000000..527c735c --- /dev/null +++ b/errors/codes/80007.md @@ -0,0 +1,5 @@ +--- +code: 80007 +title: Connection continuity not guaranteed as message limit exceeded +summary: A connection was resumed, but more messages accumulated during the disconnection than can be held for recovery. The connection continues, but some of those messages may not be redelivered, so continuity across the gap is not guaranteed. +--- diff --git a/errors/codes/80008.md b/errors/codes/80008.md new file mode 100644 index 00000000..ead4ecf0 --- /dev/null +++ b/errors/codes/80008.md @@ -0,0 +1,19 @@ +--- +code: 80008 +title: Connection recovery failed on an outdated SDK +summary: An older Ably SDK could not recover a dropped connection. This code is produced only by SDK versions that use a retired version of the Ably protocol; current SDKs connect differently and do not report it. +--- + +## What you should do + +Update the Ably SDK to a current version. This error comes only from older SDKs that connect using an earlier version of the Ably protocol, which is being [retired](https://ably.com/docs/platform/deprecate/protocol-v1), so upgrading is the durable fix and is worth doing regardless of this error. + +Until you upgrade, it is not fatal. When recovery fails, Ably still establishes a fresh connection, so re-attaching your channels and continuing works. Messages published while the original connection was gone are not recovered, so retrieve any that matter from [history](https://ably.com/docs/storage-history/storage). + +## Why it happens + +An older SDK recovers a dropped connection by asking Ably to resume it from where it left off. This error means the connection could not be resumed, most often because too much time had passed and its state had already been discarded; recovery is only possible for [a short time](https://ably.com/docs/connect/states) after a connection drops. + +## What you'll see + +The failure is reported as a non-fatal error on the new connection rather than preventing it. The error carries code 80008, commonly with the message `Unable to recover connection (connection expired)`. diff --git a/errors/codes/80009.md b/errors/codes/80009.md new file mode 100644 index 00000000..ed72b180 --- /dev/null +++ b/errors/codes/80009.md @@ -0,0 +1,5 @@ +--- +code: 80009 +title: Connection not established +summary: An operation that requires an open connection was attempted while the client was not connected. +--- diff --git a/errors/codes/80010.md b/errors/codes/80010.md new file mode 100644 index 00000000..f4cf8dad --- /dev/null +++ b/errors/codes/80010.md @@ -0,0 +1,5 @@ +--- +code: 80010 +title: Invalid operation on the connection +summary: An operation was attempted on a connection that was not in a valid state to carry it, such as one that had already been closed or replaced. +--- diff --git a/errors/codes/80011.md b/errors/codes/80011.md new file mode 100644 index 00000000..c800daa4 --- /dev/null +++ b/errors/codes/80011.md @@ -0,0 +1,5 @@ +--- +code: 80011 +title: Connection state recovery failed due to incompatible auth +summary: A dropped connection could not be recovered because the authentication details presented on the new connection did not match those of the original, so the previous connection could not be continued. +--- diff --git a/errors/codes/80012.md b/errors/codes/80012.md new file mode 100644 index 00000000..e2abc461 --- /dev/null +++ b/errors/codes/80012.md @@ -0,0 +1,5 @@ +--- +code: 80012 +title: Connection continuity not guaranteed as serial was invalid +summary: A connection was resumed without a valid connection serial to identify the point it had reached, so there was no reliable position to continue from. The connection continues, but continuity across the gap is not guaranteed. +--- diff --git a/errors/codes/80013.md b/errors/codes/80013.md new file mode 100644 index 00000000..b28184cb --- /dev/null +++ b/errors/codes/80013.md @@ -0,0 +1,5 @@ +--- +code: 80013 +title: Protocol error +summary: A message exchanged over the connection did not conform to the Ably protocol. The connection received something it could not interpret as a valid protocol message. +--- diff --git a/errors/codes/80014.md b/errors/codes/80014.md new file mode 100644 index 00000000..b6e49fb1 --- /dev/null +++ b/errors/codes/80014.md @@ -0,0 +1,5 @@ +--- +code: 80014 +title: Connection timed out +summary: The connection was not established, or a response was not received, within the time allowed. This often points to a slow or unreliable network path between the client and Ably. +--- diff --git a/errors/codes/80015.md b/errors/codes/80015.md new file mode 100644 index 00000000..632a4029 --- /dev/null +++ b/errors/codes/80015.md @@ -0,0 +1,5 @@ +--- +code: 80015 +title: Incompatible connection parameters +summary: The connection could not be established because the parameters supplied when opening it were not compatible with one another or with what Ably supports. +--- diff --git a/errors/codes/80016.md b/errors/codes/80016.md new file mode 100644 index 00000000..cd6d53d6 --- /dev/null +++ b/errors/codes/80016.md @@ -0,0 +1,5 @@ +--- +code: 80016 +title: Connection replaced by a newer one +summary: An operation was attempted on a connection that was no longer current — a newer connection had replaced it, or its transport handle had been recycled — so the operation could not be applied and the client re-establishes to continue. +--- diff --git a/errors/codes/80017.md b/errors/codes/80017.md new file mode 100644 index 00000000..a7b53246 --- /dev/null +++ b/errors/codes/80017.md @@ -0,0 +1,5 @@ +--- +code: 80017 +title: Connection closed +summary: The connection was closed deliberately, rather than dropped. This is the expected outcome when the connection is closed by request and is not a fault. +--- diff --git a/errors/codes/80018.md b/errors/codes/80018.md new file mode 100644 index 00000000..4c62d3ba --- /dev/null +++ b/errors/codes/80018.md @@ -0,0 +1,19 @@ +--- +code: 80018 +title: Invalid connection key +summary: A client tried to re-use a previous connection ID with a connection key, but the key was not in a valid format, so it could not keep that connection ID and was given a new one. +--- + +## What you should do + +The connection itself is fine: the client still connects, just with a new connection ID rather than continuing the previous one. The one effect to be aware of is presence, where a member is identified by its connection ID, so the client re-enters as a new member. + +Whether to act depends on the SDK. With a current SDK, the key is one the SDK manages itself while resuming a dropped connection, so a malformed one is unexpected and points to a problem in the SDK rather than your configuration. Supplying a recovery key yourself, through the [`recover` option](https://ably.com/docs/connect/states), applies only to older SDKs; there, an invalid key means recovery never succeeds, so confirm it is stored and restored intact, not truncated, re-encoded, or replaced. + +## Why it happens + +The connection key supplied could not be parsed. This is a structural problem with the value itself: the wrong string, a corrupted one, or one altered in transit or storage, rather than a key that is merely too old. A valid key whose connection has expired is reported as 80008 instead. The same malformed key supplied on a REST publish is reported as 40006, because there the request fails rather than continuing on a fresh connection. + +## What you'll see + +The error is reported with code 80018 and HTTP status 400, with a message of the form `invalid connection key`. When it occurs while recovering or resuming a connection, the connection reaches the connected state but with a new connection ID, rather than continuing the previous one. diff --git a/errors/codes/80019.md b/errors/codes/80019.md new file mode 100644 index 00000000..52fc13fd --- /dev/null +++ b/errors/codes/80019.md @@ -0,0 +1,5 @@ +--- +code: 80019 +title: Token request failed +summary: The Ably SDK failed to retrieve a token from the configured authUrl or authCallback. +--- diff --git a/errors/codes/80020.md b/errors/codes/80020.md new file mode 100644 index 00000000..b536ef04 --- /dev/null +++ b/errors/codes/80020.md @@ -0,0 +1,22 @@ +--- +code: 80020 +title: Continuity lost as message delivery rate exceeded +summary: Messages were dropped because they were being delivered to the connection faster than its per-connection rate limit allows. Continuity is lost on the affected channels: they keep receiving new messages, but the dropped ones are not redelivered. +--- + +## What you should do + +What to do depends on whether your application can tolerate the gap: + +- If it can, no action is needed and the error can be ignored. +- If it cannot, fetch the messages the connection did not receive from [history](https://ably.com/docs/storage-history/history) when it encounters this error. + +To stop hitting the limit, consider spreading subscriptions across more connections, so that each one receives fewer messages. If a connection genuinely needs to receive messages at a higher rate, you can [request a higher limit](https://ably.com/docs/general/limits). + +## Why it happens + +Each connection has a maximum rate at which Ably will deliver messages to it, set by your account's limits. When a connection is subscribed to more message traffic than that allows, Ably cannot deliver all of it, so it drops the excess and signals the gap to the affected channels as a continuity loss rather than silently skipping messages. + +## What you'll see + +The affected channels report a continuity loss but stay attached. The error is reported with code 80020 and HTTP status 400, with a message of the form `Continuity loss on channel as maximum server-to-client message rate exceeded; permitted rate = ...`. diff --git a/errors/codes/80021.md b/errors/codes/80021.md new file mode 100644 index 00000000..07e42d07 --- /dev/null +++ b/errors/codes/80021.md @@ -0,0 +1,5 @@ +--- +code: 80021 +title: Account-wide connection creation rate exceeded +summary: A connection was refused because new connections were being opened across the account faster than the permitted rate. The limit applies to the account as a whole rather than to any single connection. +--- diff --git a/errors/codes/80022.md b/errors/codes/80022.md new file mode 100644 index 00000000..a6987eee --- /dev/null +++ b/errors/codes/80022.md @@ -0,0 +1,5 @@ +--- +code: 80022 +title: Connection not found +summary: A request referred to a connection that the server could not find, so the exchange could not continue and the client is signalled to reconnect. This is a lookup failure, not a loss of message continuity. +--- diff --git a/errors/codes/80023.md b/errors/codes/80023.md new file mode 100644 index 00000000..8f506f14 --- /dev/null +++ b/errors/codes/80023.md @@ -0,0 +1,21 @@ +--- +code: 80023 +title: Connection re-established in a different region +summary: The client reconnected to a different Ably region from the one it was using, so it could not keep its connection ID and was given a new one. +--- + +## What you should do + +Nothing. The connection is established normally and keeps working. The difference is that it has a new connection ID rather than continuing the previous one. + +The only thing to be aware of is presence: a presence member is identified by both its client ID and connection ID, so when the client re-enters presence after reconnecting in a different region it appears as a new member rather than the one it was registered as before. + +## Why it happens + +When a client briefly drops its connection, it can reconnect and carry on with the same connection ID rather than being assigned a new one. + +A connection ID cannot be carried across Ably regions, and the main reason is presence. When a client enters presence, its membership is registered against its connection ID and tracked by the region it is connected to, and Ably checks whether those members are still present only within that region, not across regions — which keeps regions independent of one another. So if the client reconnects to a different region, it is issued a new connection ID instead of keeping the previous one. + +## What you'll see + +The connection reaches the connected state, so this surfaces as the connection's error reason rather than a failure. It is reported with code 80023 and HTTP status 400, with the message `Unable to resume connection from another site` — "site" here is the internal term for a region. diff --git a/errors/codes/80024.md b/errors/codes/80024.md new file mode 100644 index 00000000..12aa2d17 --- /dev/null +++ b/errors/codes/80024.md @@ -0,0 +1,22 @@ +--- +code: 80024 +title: Outdated Ably SDK version +summary: The connecting Ably SDK is an old version, and the way it connects to Ably is being retired. Connections from these SDK versions increasingly fail until they can no longer connect. +--- + +## What you should do + +Update the Ably SDK you are using to a supported version. See the [deprecation notice](https://ably.com/docs/platform/deprecate/protocol-v1) for the versions affected and the ones to upgrade to. + +## Why it happens + +Older versions of the Ably SDKs connect to Ably using an earlier version of the Ably protocol. Ably is retiring it in favour of a newer version that is more efficient and scalable and supports features the older one cannot. + +## What you'll see + +The error is reported with code 80024 and HTTP status 400. You may see it only intermittently at first: the retirement is rolled out gradually, so for a period some connections using the old protocol version fail while others still succeed. There are two messages, depending on the stage of the retirement: + +- While the connection still succeeds, it carries the warning `Support for this protocol version will be removed imminently, please update your SDK immediately`. +- Once that protocol version is being removed, the connection is refused with `This protocol version is no longer supported, please update your SDK version to connect`. + +The "protocol version" named in these messages corresponds to your installed SDK version — the thing you update to resolve this. diff --git a/errors/codes/80030.md b/errors/codes/80030.md new file mode 100644 index 00000000..828cc456 --- /dev/null +++ b/errors/codes/80030.md @@ -0,0 +1,5 @@ +--- +code: 80030 +title: Connection failed due to a client restriction +summary: The connection was refused because a restriction placed on the client was not met. Restrictions can limit which clients are allowed to connect based on conditions set for the application. +--- diff --git a/errors/codes/90000.md b/errors/codes/90000.md new file mode 100644 index 00000000..cfef5ae2 --- /dev/null +++ b/errors/codes/90000.md @@ -0,0 +1,5 @@ +--- +code: 90000 +title: Channel operation failed +summary: A channel operation, such as attaching, detaching, or publishing, could not be completed. +--- diff --git a/errors/codes/90001.md b/errors/codes/90001.md new file mode 100644 index 00000000..2145bcdd --- /dev/null +++ b/errors/codes/90001.md @@ -0,0 +1,5 @@ +--- +code: 90001 +title: Channel in an invalid state for the operation +summary: A channel operation was attempted while the channel was not in a state that permits it, such as publishing or performing an action on a channel that is not currently attached. +--- diff --git a/errors/codes/90002.md b/errors/codes/90002.md new file mode 100644 index 00000000..fe792536 --- /dev/null +++ b/errors/codes/90002.md @@ -0,0 +1,5 @@ +--- +code: 90002 +title: Channel history could not be retrieved +summary: A request for a channel's message history could not be completed, because the position it asked to read from could not be resolved — either the query was incomplete or the point requested is no longer retained. +--- diff --git a/errors/codes/90003.md b/errors/codes/90003.md new file mode 100644 index 00000000..1631c548 --- /dev/null +++ b/errors/codes/90003.md @@ -0,0 +1,22 @@ +--- +code: 90003 +title: Channel continuity not guaranteed +summary: The client resumed a channel after being disconnected long enough that the point it had reached is no longer available to resume from. The channel reattaches from the earliest point still available, but continuity up to that point is not guaranteed. +--- + +## What you should do + +The channel stays attached and keeps delivering new messages, so nothing is broken. Whether to act on the messages that may have been missed depends on your application: + +- If it can tolerate occasionally missing some messages, no action is needed and the error can be ignored. +- If it cannot tolerate missing messages, enable [persistence](https://ably.com/docs/storage-history/storage) on the channel and have your application fetch the messages it might have missed from [history](https://ably.com/docs/storage-history/history) whenever it encounters this error. + +## Why it happens + +Clients keep track of the last message they received on a channel. When they reconnect they automatically tell Ably which message that was, so that they can receive any messages they may have missed. + +Ably only keeps previous messages for a short time — [around two minutes](https://ably.com/docs/connect/states). If a client is disconnected for longer than that, the message it had reached, along with all others older than two minutes, are gone. Ably then reattaches the client at the earliest point still available, and any messages published before that point may not have reached it. + +## What you'll see + +The error is reported with code 90003 and HTTP status 404. The message reads `Unable to recover channel (messages expired)` — despite that wording, it means continuity is not guaranteed, not that messages are known to have expired. diff --git a/errors/codes/90004.md b/errors/codes/90004.md new file mode 100644 index 00000000..5695c59a --- /dev/null +++ b/errors/codes/90004.md @@ -0,0 +1,22 @@ +--- +code: 90004 +title: Channel backlog too large +summary: The client requested to resume or rewind a channel whose backlog held more messages than a single replay can deliver. The channel attaches, but the messages beyond that limit are not replayed. +--- + +## What you should do + +The channel stays attached and keeps delivering new messages, so nothing is broken. Whether to act on the messages that weren't replayed depends on your application: + +- If it can tolerate missing them, no action is needed and the error can be ignored. +- If it cannot tolerate missing them, enable [persistence](https://ably.com/docs/storage-history/storage) on the channel and have your application fetch the messages it did not receive from [history](https://ably.com/docs/storage-history/history) whenever it encounters this error. + +## Why it happens + +When a client resumes or [rewinds](https://ably.com/docs/channels/options/rewind) a channel, Ably replays the messages published in the requested range. There is a limit on how many it can replay at once. If the range holds more than that, Ably delivers the most recent messages up to the limit, and the older ones beyond it are not replayed. + +This is most likely on high-throughput channels, or when recovering over a long or busy period — for example a `rewind` covering more messages than the limit. + +## What you'll see + +The error is reported with code 90004 and HTTP status 404, with the message `Unable to recover channel (message limit exceeded)`. diff --git a/errors/codes/90005.md b/errors/codes/90005.md new file mode 100644 index 00000000..bc3f64ff --- /dev/null +++ b/errors/codes/90005.md @@ -0,0 +1,22 @@ +--- +code: 90005 +title: Channel resumed in a different region +summary: The client reconnected to a different Ably region from the one it was using, so the position it resumed from could not be applied. The channel attaches at the current point, and no earlier messages are replayed. +--- + +## What you should do + +The channel stays attached and keeps delivering new messages, so nothing is broken. Whether to act on the messages that weren't replayed depends on your application: + +- If it can tolerate missing them, no action is needed and the error can be ignored. +- If it cannot tolerate missing them, enable [persistence](https://ably.com/docs/storage-history/storage) on the channel and have your application fetch the messages it did not receive from [history](https://ably.com/docs/storage-history/history) whenever it encounters this error. + +## Why it happens + +Clients keep track of the last message they received on a channel. When they reconnect they automatically tell Ably which message that was, so that they can receive any messages they may have missed. + +The record of the order of messages is specific to the region each client was connected to. If a client reconnects to a different region, there is no way to tell which messages it had already received. In this case, the client won't receive any missed messages, only those published from the point it reconnected. + +## What you'll see + +The error is reported with code 90005 and HTTP status 404, with the message `Unable to recover channel (unable to resume from a different site)` — "site" here is the internal term for a region. diff --git a/errors/codes/90006.md b/errors/codes/90006.md new file mode 100644 index 00000000..2a2f0361 --- /dev/null +++ b/errors/codes/90006.md @@ -0,0 +1,5 @@ +--- +code: 90006 +title: Channel history request had no limit +summary: A request for a channel's message history could not be served because it asked for the complete history with no limit on how many messages to return. +--- diff --git a/errors/codes/90007.md b/errors/codes/90007.md new file mode 100644 index 00000000..5a76ced8 --- /dev/null +++ b/errors/codes/90007.md @@ -0,0 +1,5 @@ +--- +code: 90007 +title: Channel operation timed out +summary: A channel attach or detach did not complete within the expected time because no response was received from Ably. The operation may not have taken effect, and is usually the result of a transient interruption. +--- diff --git a/errors/codes/90008.md b/errors/codes/90008.md new file mode 100644 index 00000000..a36f88e1 --- /dev/null +++ b/errors/codes/90008.md @@ -0,0 +1,29 @@ +--- +code: 90008 +title: Attach point too old for untilAttach history +summary: A history request using untilAttach relies on the channel's attach point still being among recent messages. In this case it was not, so the request could not be served and no messages were returned. +--- + +## What you should do + +Whether this matters depends on whether the channel has [persistence](https://ably.com/docs/storage-history/storage) enabled. + +Without persistence, there is nothing to do and the error can be ignored. The client already has all recent messages, because it has received everything since its attach point and that point is older than the recent window. The older history [untilAttach](https://ably.com/docs/storage-history/history) would have returned is not retained, so there is nothing more to recover. + +With persistence, the older history untilAttach would have returned is retained, and can be fetched with a plain [history](https://ably.com/docs/storage-history/history) request without untilAttach. That response won't line up exactly with the messages the client already received from the channel, so choose how to reconcile them: + +- **Remove duplicates yourself.** Discard any messages in the response that the client already received. This keeps a gap-free record but your application has to deduplicate. +- **Accept possible duplicates.** Less work, but some messages may appear both in the response and among those already received. +- **Leave the older messages out.** If the client does not need them, continue with only the messages it already received. + +Which is best depends on whether duplicate messages or missing history is worse for your use case. + +## Why it happens + +untilAttach returns history up to the attach point, and it works from Ably's window of recent messages — about two minutes. It can therefore only serve an attach point that still falls within that window. + +This error means the attach point was older than that, so untilAttach could not use it. The messages themselves are unaffected; whether the older ones can still be retrieved depends on the channel's persistence. + +## What you'll see + +The whole request fails and no messages are returned. The error is reported with code 90008 and HTTP status 400, with the message `Unable to get channel history: attach point not found`. diff --git a/errors/codes/90009.md b/errors/codes/90009.md new file mode 100644 index 00000000..efa9ca6c --- /dev/null +++ b/errors/codes/90009.md @@ -0,0 +1,5 @@ +--- +code: 90009 +title: Subscribe operation failed as subscribe mode not enabled +summary: Messages could not be delivered to a subscriber because the channel had not requested the subscribe mode in its channel options. Messages are only delivered to clients that request that mode, so the listener never fires. +--- diff --git a/errors/codes/90010.md b/errors/codes/90010.md new file mode 100644 index 00000000..7b32ee98 --- /dev/null +++ b/errors/codes/90010.md @@ -0,0 +1,5 @@ +--- +code: 90010 +title: Too many channels +summary: An operation was rejected because it would exceed the maximum number of channels permitted, either the channels attached to a single connection or those included in a single batch request. +--- diff --git a/errors/codes/90021.md b/errors/codes/90021.md new file mode 100644 index 00000000..63ee3121 --- /dev/null +++ b/errors/codes/90021.md @@ -0,0 +1,5 @@ +--- +code: 90021 +title: Account-wide channel creation rate exceeded +summary: Requests were rejected because the rate at which new channels were being created across the account exceeded the permitted limit. The limit counts channels newly activated within a period, not those already active. +--- diff --git a/errors/codes/91000.md b/errors/codes/91000.md new file mode 100644 index 00000000..0879398c --- /dev/null +++ b/errors/codes/91000.md @@ -0,0 +1,5 @@ +--- +code: 91000 +title: Cannot enter presence without a clientId +summary: A request to enter the presence set was rejected because the connection had no clientId. Presence members are identified by their clientId, so one is required to enter. +--- diff --git a/errors/codes/91001.md b/errors/codes/91001.md new file mode 100644 index 00000000..4ee10e69 --- /dev/null +++ b/errors/codes/91001.md @@ -0,0 +1,5 @@ +--- +code: 91001 +title: Cannot enter presence in the channel's current state +summary: A request to enter the presence set was rejected because the channel was not in a state that permits presence operations, such as a channel that is detached, suspended, or failed. +--- diff --git a/errors/codes/91002.md b/errors/codes/91002.md new file mode 100644 index 00000000..a1a78378 --- /dev/null +++ b/errors/codes/91002.md @@ -0,0 +1,5 @@ +--- +code: 91002 +title: Cannot leave presence that was never entered +summary: A request to leave the presence set was rejected because the clientId was not a member of it. +--- diff --git a/errors/codes/91003.md b/errors/codes/91003.md new file mode 100644 index 00000000..03087f34 --- /dev/null +++ b/errors/codes/91003.md @@ -0,0 +1,5 @@ +--- +code: 91003 +title: Too many presence members +summary: A request to enter the presence set was rejected because the channel already held the maximum number of presence members permitted. The limit counts the members present on a single channel. +--- diff --git a/errors/codes/91004.md b/errors/codes/91004.md new file mode 100644 index 00000000..f13f4338 --- /dev/null +++ b/errors/codes/91004.md @@ -0,0 +1,5 @@ +--- +code: 91004 +title: Unable to automatically re-enter presence +summary: After reconnecting, the Ably SDK tried to re-enter the presence set on the client's behalf but the attempt did not succeed, so the member was not restored to the presence set. +--- diff --git a/errors/codes/91005.md b/errors/codes/91005.md new file mode 100644 index 00000000..d9fa6dac --- /dev/null +++ b/errors/codes/91005.md @@ -0,0 +1,5 @@ +--- +code: 91005 +title: Presence state out of sync +summary: The presence set held by the client no longer matched the state on Ably. The presence members are re-synchronised so the two are brought back into agreement. +--- diff --git a/errors/codes/91006.md b/errors/codes/91006.md new file mode 100644 index 00000000..ed48437d --- /dev/null +++ b/errors/codes/91006.md @@ -0,0 +1,5 @@ +--- +code: 91006 +title: Total presence set data too large +summary: A request to enter the presence set was rejected because the combined size of the data held by members on the channel would exceed the maximum permitted for the presence set. +--- diff --git a/errors/codes/91007.md b/errors/codes/91007.md new file mode 100644 index 00000000..0891d10f --- /dev/null +++ b/errors/codes/91007.md @@ -0,0 +1,19 @@ +--- +code: 91007 +title: Unexpected loss of presence membership +summary: The connection was removed from the presence set despite still being active, so the channel was detached to prompt the Ably SDK to automatically re-enter presence. The condition is usually transient. +--- + +## What you should do + +Usually nothing. A client that entered presence re-enters it automatically after this error: the channel detaches, re-attaches, and the member rejoins the presence set without any action. + +There is nothing in your application to change, because the cause is internal to Ably rather than your configuration. If this error is having a negative impact on your application, [report it to Ably](https://ably.com/support). + +## Why it happens + +During routine changes on Ably's side, such as a failover or a rebalancing of capacity, a presence member can be dropped from the presence set even though its connection is still active. Ably detects the inconsistency and detaches the channel so the client re-attaches and re-enters presence, restoring the member. + +## What you'll see + +The channel is detached and then re-attached, and the connection re-enters presence; published messages and other channels are unaffected. The error is reported with code 91007 and HTTP status 500, with the message `Unexpected loss of presence membership information for this connection, requesting reattach to trigger automatic presence reentry`. diff --git a/errors/codes/91008.md b/errors/codes/91008.md new file mode 100644 index 00000000..e7307ef4 --- /dev/null +++ b/errors/codes/91008.md @@ -0,0 +1,5 @@ +--- +code: 91008 +title: Presence operation failed as presence_subscribe mode not enabled +summary: The presence members of a channel could not be retrieved because the channel had not requested the presence_subscribe mode in its channel options. Presence state is only delivered to clients that request that mode. +--- diff --git a/errors/codes/91100.md b/errors/codes/91100.md new file mode 100644 index 00000000..6eefd243 --- /dev/null +++ b/errors/codes/91100.md @@ -0,0 +1,5 @@ +--- +code: 91100 +title: Presence member left as their connection closed +summary: A presence member was removed from the presence set because it was no longer present, usually because the connection it entered on was closed. +--- diff --git a/errors/codes/92000.md b/errors/codes/92000.md new file mode 100644 index 00000000..f2197679 --- /dev/null +++ b/errors/codes/92000.md @@ -0,0 +1,5 @@ +--- +code: 92000 +title: Invalid LiveObjects message +summary: A LiveObjects message was rejected because it was invalid or did not conform to the expected structure. This usually indicates a problem with how the object operation was constructed before it was sent. +--- diff --git a/errors/codes/92001.md b/errors/codes/92001.md new file mode 100644 index 00000000..a2ad8f1b --- /dev/null +++ b/errors/codes/92001.md @@ -0,0 +1,5 @@ +--- +code: 92001 +title: LiveObjects limit exceeded +summary: A LiveObjects operation was rejected because it would take the channel beyond the maximum number of objects it is allowed to hold. The limit is set by the account. +--- diff --git a/errors/codes/92002.md b/errors/codes/92002.md new file mode 100644 index 00000000..59d0ba85 --- /dev/null +++ b/errors/codes/92002.md @@ -0,0 +1,5 @@ +--- +code: 92002 +title: LiveObjects operation on a deleted object +summary: An operation could not be applied because the target LiveObject had already been deleted. A deleted object is retained only as a marker, or tombstone, and can no longer be modified. +--- diff --git a/errors/codes/92003.md b/errors/codes/92003.md new file mode 100644 index 00000000..ac78a44c --- /dev/null +++ b/errors/codes/92003.md @@ -0,0 +1,5 @@ +--- +code: 92003 +title: LiveObjects root has been deleted +summary: A LiveObjects object tree could not be fetched because the object at its root had already been deleted. A deleted object is retained only as a marker, or tombstone, and cannot serve as the root of a tree. +--- diff --git a/errors/codes/92004.md b/errors/codes/92004.md new file mode 100644 index 00000000..2030ba20 --- /dev/null +++ b/errors/codes/92004.md @@ -0,0 +1,5 @@ +--- +code: 92004 +title: LiveObjects object not found +summary: A LiveObjects operation referenced an object that does not exist on the channel. The object may never have been created, or it may have been removed before the operation was applied. +--- diff --git a/errors/codes/92005.md b/errors/codes/92005.md new file mode 100644 index 00000000..4cdab935 --- /dev/null +++ b/errors/codes/92005.md @@ -0,0 +1,5 @@ +--- +code: 92005 +title: LiveObjects path matched no objects +summary: A LiveObjects operation specified a path within an object tree that did not resolve to any object. The path may be incorrect, or the objects it points to may not exist. +--- diff --git a/errors/codes/92006.md b/errors/codes/92006.md new file mode 100644 index 00000000..2f03ebf6 --- /dev/null +++ b/errors/codes/92006.md @@ -0,0 +1,5 @@ +--- +code: 92006 +title: LiveObjects operation missing object reference +summary: A LiveObjects operation could not be performed because it specified neither an object identifier nor a path. +--- diff --git a/errors/codes/92007.md b/errors/codes/92007.md new file mode 100644 index 00000000..4d0bf5a2 --- /dev/null +++ b/errors/codes/92007.md @@ -0,0 +1,5 @@ +--- +code: 92007 +title: LiveObjects operation not valid for the path +summary: A LiveObjects operation could not be applied to the object at the specified path, because the operation is not compatible with the kind of object found there. +--- diff --git a/errors/codes/92008.md b/errors/codes/92008.md new file mode 100644 index 00000000..218672a8 --- /dev/null +++ b/errors/codes/92008.md @@ -0,0 +1,5 @@ +--- +code: 92008 +title: LiveObjects sync did not complete +summary: A LiveObjects operation could not be applied because the channel had not finished synchronising its objects. +--- diff --git a/errors/codes/93001.md b/errors/codes/93001.md new file mode 100644 index 00000000..dcd9f0d7 --- /dev/null +++ b/errors/codes/93001.md @@ -0,0 +1,5 @@ +--- +code: 93001 +title: Annotation listener without annotation_subscribe mode +summary: An annotation listener was added to a channel that had not requested the annotation_subscribe mode in its channel options. Annotations are only delivered to clients that explicitly request them, so the listener will not receive anything. +--- diff --git a/errors/codes/93002.md b/errors/codes/93002.md new file mode 100644 index 00000000..95a5dc40 --- /dev/null +++ b/errors/codes/93002.md @@ -0,0 +1,5 @@ +--- +code: 93002 +title: Message annotations, updates, appends, and deletes not enabled +summary: An operation could not be performed because it requires a feature that is enabled by the channel namespace's 'Message annotations, updates, appends, and deletes' setting. +--- diff --git a/errors/guidelines.md b/errors/guidelines.md new file mode 100644 index 00000000..bae05c32 --- /dev/null +++ b/errors/guidelines.md @@ -0,0 +1,137 @@ +# Error documentation guidelines + +This directory contains the user-facing documentation for each error code in the system. Each error has its own file, and the contents are used to render both the **table view** (a scannable list of errors) and the **detail page** (a dedicated page for a single error) in the UI. + +These guidelines cover how to write error entries and how the source files are structured. + +## File layout + +- One file per error code, named `.md` (e.g. `40142.md`). +- The filename must match the `code` field in the frontmatter exactly. +- Files live in the [`codes/`](./codes) subdirectory; nothing there is loaded recursively. + +## File format + +Each file is a Markdown document with YAML frontmatter: + +```markdown +--- +code: 40142 +title: Token expired +summary: The client's connection or request was rejected because the authentication token had expired. Each token has an expiry time that is set when it is issued. +--- +``` + +### Frontmatter fields + +| Field | Required | Description | +|---|---|---| +| `code` | Yes | The error code. Must match the filename. | +| `title` | Yes | A short noun phrase describing the error. See [Writing titles](#writing-titles). | +| `summary` | Yes | A one or two sentence plain-prose summary. See [Writing summaries](#writing-summaries). | + +A Markdown body is permitted below the frontmatter and provides the detail-page content beneath the title and summary — whether the reader needs to act, why the error happens, and how to resolve it. See [Writing the body](#writing-the-body). + +## How the source maps to the UI + +The same file feeds two surfaces: + +**Table view.** Renders `code`, `title`, and `summary` as a row. + +**Detail page.** Renders the title as the page heading, the summary as a lede paragraph beneath it, and the body (if present) beneath that. + +This is why the summary lives in frontmatter rather than as a body section: it has strict rules (length, plain prose, no formatting) and is reused across both views. + +## Writing titles + +The title's job is to let a user decide, in under a second, whether this error is the one they care about. + +- **Lead with what happened, from the user's perspective.** "Connection refused by server" beats "ECONNREFUSED returned." +- **Keep it short.** Aim for 3–7 words; hard cap at 10. If you can't fit the meaning, the summary is where the detail goes. +- **Prefer short phrases; a concise subject–verb clause is fine.** Titles don't have to be strict noun phrases — "Connection disconnected", "Integration invocation failed", and "Token expired" all read well. Avoid long, fully-punctuated sentences and second-person phrasing: "Token expired", not "Your token has expired." +- **Be specific enough to disambiguate.** If three errors could honestly be titled "Authentication failed," the titles aren't doing their job. Prefer "Token expired", "Invalid credentials", "Account locked" +- **Use sentence case.** "Token expired", not "Token Expired" or "TOKEN EXPIRED". +- **No trailing punctuation.** +- **Don't restate the code.** The code is shown alongside the title; "40142 error" is wasted space. + +## Writing summaries + +The summary's job is to give a user who's interested enough to stop scanning the next layer of understanding — what the error means and (when known) why it might have happened. + +- **One or two sentences, roughly 15–40 words.** Long enough to be useful, short enough that the table doesn't become a wall of text. +- **Cover what happened, then why it might have happened.** A useful template: *"[What happened in plain language]. [Why it might have happened, or under what conditions]."* For example: "Publishing failed because the connection was suspended. Connections become suspended after being disconnected for around two minutes." Only add the second sentence when it genuinely adds a cause or condition. If it would merely restate the first in different words, leave it out — a single precise sentence beats a padded pair. +- **Don't prescribe remediation.** Imperatives like "Check…", "Verify…", "Retry…", "Sign in again…" presume a specific reader posture and read poorly in a table that one error code stands in for many occurrences of. +- **Write in plain language.** Assume the reader knows their domain but not your internal terminology. "The request was rejected because the signature didn't match" beats "HMAC validation failed against the canonical request." +- **Be concrete about cause when you know it; be honest when you don't.** "This usually happens when..." is fine. Don't invent certainty. +- **Avoid blame-y phrasing.** "The request was missing a required field" lands better than "You sent an invalid request." Passive voice is your friend here. +- **Write generically; the summary represents every occurrence.** Summaries appear in a table where one row stands in for many instances of the same code. Avoid deictic phrasing like "this token" or "your channel" that points at a single instance. Prefer generic references — "the token", "the channel", "the supplied attach point" — that read correctly regardless of which occurrence the reader has in front of them. +- **Don't repeat the title.** If the title is "Token expired," don't start with "Your token has expired." Start with the next useful piece of information. +- **No Markdown formatting.** Summaries render in table cells as plain text. +- **No error codes, stack traces, or internal identifiers** unless they're directly actionable for the user. + +### A note on YAML quoting + +Single-line summaries in YAML almost never need quoting. Just write the sentence after the `summary:` key. The exception is when the summary starts with a special character (`:`, `-`, `[`, `{`, `#`, `&`, `*`, `!`, `|`, `>`, `'`, `"`, `%`, `@`, `` ` ``) — in that case, wrap the whole summary in double quotes: + +```yaml +summary: "'Bearer' prefix missing from the Authorization header. Add it and retry." +``` + +## Writing the body + +The body is optional but recommended for any error a customer is likely to land on. It renders on the **detail page only**, beneath the title and summary — so it is written for someone who has already read both and wants the next layer: what to do about it. + +Because the title and summary come first, the body's overriding rule is: + +- **Don't restate the title or summary.** The reader has just read "what happened." Open with the next useful thing, not a recap. + +### Structure + +Use this small, consistent set of sections. Use only the ones that apply, and omit a section rather than padding it. + +1. **What you should do.** Open here — including when the answer is *nothing*: if the error is expected, transient, or self-healing, say so plainly and explain how to tell that case from one that needs fixing. If an error is always a hard failure, the answer is simply the fix. +2. **Why it happens.** The realistic causes, in the reader's terms — their configuration, their actions, their environment — not where the code is raised internally. Where a cause has a fix of its own, give it here; a fix shared across causes belongs once in *What you should do*. Where a fix is "use the feature correctly", link to that feature's documentation; **don't inline code samples** (see the rules below). +3. **What you'll see.** Last, and only as a findability aid: the fixed wording of the message(s), and the HTTP status. Quote only the stable parts — many messages also carry runtime detail (the channel, the observed rate) that varies between occurrences. This helps someone searching for the text they saw; it is of little use to someone already reading, which is why it goes at the bottom. + +### Rules + +- **Describe generically; instruct in second person.** When describing the error or its causes, keep the generic, non-deictic phrasing summaries use — "the token", not "your token" — because the page stands for every occurrence. When telling the reader what to do, second person is natural: "configure your client with `authCallback`". +- **Don't inline code samples.** Examples of how to use a feature belong in that feature's documentation, where a customer can find them *before* they hit an error — link there instead. If there is no doc to link to, that is a general documentation gap: raise it (e.g. open an issue) rather than papering over it with a one-off snippet here. +- **Link out for how-to; keep the page about the error.** The body explains the error and points at the fix; it is not a tutorial. Link to maintained values (limits, durations) rather than restating them, so the page can't drift. +- **Weave links into the prose.** Link the words that name the thing — "enable [persistence](...)", "[around two minutes](...)" — rather than appending a separate "(see ...)" aside. The sentence should still read naturally if the link markup were stripped out. +- **Verify facts against their source; don't cite it.** Check message strings, status codes, and limits against the raising code or `errors.json` before writing them — never from memory. Those are how you verify, not what you link: the page links only to customer-facing docs. Confirm those links resolve. +- **Tight, not exhaustive.** Include only what serves the decision and the fix. A good body is often three or four short paragraphs. +- **Terminology and tone** follow the same rules as every other field — use the [Dictionary of terms](https://ably.atlassian.net/wiki/spaces/devex/pages/4295262228/Dictionary+of+terms), and keep the voice plain and calm. + +[`40142.md`](./40142.md) is a complete worked example of a title, summary, and body together. + +## Terminology + +Use the customer-facing terms from the internal [Dictionary of terms](https://ably.atlassian.net/wiki/spaces/devex/pages/4295262228/Dictionary+of+terms). The dictionary is the source of truth; the table below highlights the substitutions that come up most often in error copy. Don't take wording from the error's internal message string or the code that raises it — use those to understand the error, then name it as customers would. + +| Use | Don't use | Notes | +|---|---|---| +| Region | Site, datacenter, cluster | The geographic AWS location that a resource runs in (e.g. `eu-west-1`). "Site" and "cluster" are internal infrastructure concepts and should almost never appear in customer-facing copy. | +| Ably [Product] [Language] SDK | Client library, ably-js | E.g. "Ably Pub/Sub JavaScript SDK". | +| Ably Platform | Data Streaming Network, DSN | The technology and infrastructure that delivers the Ably Service. | +| Connection state recovery | Stream resume | Plain "resume" is fine for resuming a connection — only "stream resume" is disallowed. | +| Token authentication, Basic authentication | Key authentication | | +| Inbound / Outbound webhook | Incoming / Outgoing webhook | | +| Integration | Reactor, Firehose, Integration rule | "Rule" now refers to channel rules (per-namespace settings), a different concept; don't call an integration a rule. | +| Ably error code | Error code | When referring to a code in this documentation, qualify it. | + +If you need a term that isn't covered here, check the dictionary before inventing one. + +## Tone, across all fields + +- **Plain, calm, and specific.** The reader is often stressed, in a hurry, or unfamiliar with the system. Optimise for them. +- **Consistent voice across errors.** When 15 of these are read end-to-end, they should feel like they were written by one person. Inconsistency across teams is the most common failure mode here. +- **Treat error copy as product copy, not log messages.** It's worth a second pair of eyes — engineers aren't always the best judges of what reads naturally to a user. + +## Reviewing changes + +When opening a PR that adds or edits an error entry: + +1. **Read the table view in your head.** Does the title disambiguate this error from neighbours? Does the summary stand on its own? +2. **Compare against existing entries.** Open two or three other files side-by-side. Does your new entry match their tone and structure? +3. **Check the validation output.** CI will flag missing fields, length violations, filename/code mismatches, and duplicates. From 3a9848590a5e0a9250348ee8893bc349687d4be6 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 03:08:43 +0100 Subject: [PATCH 3/9] Validate the error registry in CI Add errors/scripts/validate-errors.js: checks every codes/.md has a matching filename/code, required code/title/summary fields, and that every errors.json code has a registry file (completeness). Wire it into the check workflow via the validate:errors npm script. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/check.yml | 1 + errors/scripts/validate-errors.js | 182 ++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 184 insertions(+) create mode 100644 errors/scripts/validate-errors.js diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index d02135fd..ea832e06 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -25,6 +25,7 @@ jobs: npm ci npm run lint npm run validate:agents + npm run validate:errors npm test check-go: diff --git a/errors/scripts/validate-errors.js b/errors/scripts/validate-errors.js new file mode 100644 index 00000000..03957021 --- /dev/null +++ b/errors/scripts/validate-errors.js @@ -0,0 +1,182 @@ +#!/usr/bin/env node + +/* + * Validates the error-code registry in `errors/codes/`. + * + * The set of `.md` files in `errors/codes/` is the source of truth for + * which error codes are valid. This script enforces the invariants that must + * hold for every file, and cross-checks completeness against the legacy + * `protocol/errors.json` map while that still exists. + * + * Two tiers: + * - Errors fail CI (exit 1): unparseable frontmatter, missing/blank `code`, + * `title`, or `summary`, filename/`code` mismatch, non-integer `code`, and + * any `errors.json` code with no `codes/.md` (completeness). + * - Warnings do not fail CI: a `codes/.md` whose code is absent from + * `errors.json` (curated ahead of the map, or the map is being retired), + * and soft length checks on `title`/`summary` per guidelines.md. + * + * A valid registry entry requires `code`, `title`, and `summary` (per + * guidelines.md); a Markdown body is optional. + */ + +const fs = require('fs'); +const path = require('path'); + +const ERRORS_DIR = path.resolve(__dirname, '..'); +const CODES_DIR = path.join(ERRORS_DIR, 'codes'); +const ERRORS_JSON = path.resolve(ERRORS_DIR, '..', 'protocol', 'errors.json'); + +const errors = []; +const warnings = []; + +const fail = (file, message) => errors.push(`${file}: ${message}`); +const warn = (file, message) => warnings.push(`${file}: ${message}`); + +/** + * Parse the constrained YAML frontmatter used by these files: a `---` fence, + * then one `key: value` per line, then a closing `---`. Values may contain + * colons (we split on the first `: ` only) and may be wrapped in double quotes. + * + * @param {string} content - The full file contents. + * @returns {{ fields: object, hasBody: boolean } | { error: string }} Parsed + * frontmatter fields and whether a body follows, or an error description. + */ +function parseFrontmatter(content) { + if (!content.startsWith('---\n')) { + return { error: 'missing opening `---` frontmatter fence' }; + } + const rest = content.slice(4); + const end = rest.indexOf('\n---'); + if (end === -1) { + return { error: 'missing closing `---` frontmatter fence' }; + } + const block = rest.slice(0, end); + const body = rest.slice(end + 4).replace(/^\n+/, ''); + const fields = {}; + block.split('\n').forEach((raw) => { + const line = raw.trimEnd(); + if (line === '') return; + const sep = line.indexOf(': '); + const key = sep === -1 ? line.replace(/:$/, '') : line.slice(0, sep); + let value = sep === -1 ? '' : line.slice(sep + 2); + if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) { + value = value.slice(1, -1).replace(/\\"/g, '"'); + } + fields[key.trim()] = value; + }); + return { fields, hasBody: body.trim().length > 0 }; +} + +/** + * Count whitespace-separated words in a string. + * + * @param {string} text - The text to count. + * @returns {number} The number of words. + */ +function wordCount(text) { + return text.trim().split(/\s+/).filter(Boolean).length; +} + +/** + * Validate a single `.md` registry file, recording errors and warnings. + * + * @param {string} fileName - The file name within the codes directory. + * @returns {string | null} The validated code, or null if it could not be read. + */ +function validateFile(fileName) { + const codeFromName = fileName.replace(/\.md$/, ''); + const content = fs.readFileSync(path.join(CODES_DIR, fileName), 'utf8'); + const parsed = parseFrontmatter(content); + + if (parsed.error) { + fail(fileName, parsed.error); + return null; + } + const { fields } = parsed; + + // code + if (!fields.code) { + fail(fileName, 'missing required `code` frontmatter field'); + } else if (!/^\d+$/.test(fields.code)) { + fail(fileName, `\`code\` must be an integer, got "${fields.code}"`); + } else if (fields.code !== codeFromName) { + fail(fileName, `\`code\` (${fields.code}) does not match filename`); + } + + // title + if (!fields.title || fields.title.trim() === '') { + fail(fileName, 'missing required `title` frontmatter field'); + } else if (wordCount(fields.title) > 10) { + warn(fileName, `title is ${wordCount(fields.title)} words (guideline: 3–7, hard cap 10)`); + } + + // summary — required per guidelines.md; length checked against the guideline + // range when present. + if (fields.summary === undefined || fields.summary === '') { + fail(fileName, 'missing required `summary` frontmatter field'); + } else { + const words = wordCount(fields.summary); + if (words < 15 || words > 40) { + warn(fileName, `summary is ${words} words (guideline: 15–40)`); + } + } + + return fields.code && /^\d+$/.test(fields.code) ? fields.code : null; +} + +/** + * Validate every file in the registry and report the outcome, exiting non-zero + * if any hard errors were found. + * + * @returns {void} + */ +function main() { + if (!fs.existsSync(CODES_DIR)) { + console.error(`No codes directory at ${CODES_DIR}`); + process.exit(1); + } + + const files = fs.readdirSync(CODES_DIR).filter((f) => f.endsWith('.md')); + const present = new Set(); + files.forEach((file) => { + const code = validateFile(file); + if (code) present.add(code); + }); + + // Completeness + reverse cross-check against the legacy errors.json map. + if (fs.existsSync(ERRORS_JSON)) { + const map = JSON.parse(fs.readFileSync(ERRORS_JSON, 'utf8')); + const jsonCodes = new Set(Object.keys(map)); + + [...jsonCodes] + .filter((c) => !present.has(c)) + .sort((a, b) => a - b) + .forEach((code) => { + fail('registry', `errors.json defines ${code} ("${map[code]}") but codes/${code}.md is missing`); + }); + + [...present] + .filter((c) => !jsonCodes.has(c)) + .sort((a, b) => a - b) + .forEach((code) => { + warn(`codes/${code}.md`, 'code is not present in protocol/errors.json'); + }); + } else { + warn('registry', 'protocol/errors.json not found; skipping completeness cross-check'); + } + + if (warnings.length) { + console.warn(`\n${warnings.length} warning(s):`); + warnings.forEach((w) => console.warn(` ⚠ ${w}`)); + } + if (errors.length) { + console.error(`\n${errors.length} error(s):`); + errors.forEach((e) => console.error(` ✗ ${e}`)); + console.error('\nError registry validation failed.'); + process.exit(1); + } + console.log(`\n✓ Error registry valid: ${present.size} code(s) in errors/codes/.`); +} + +main(); diff --git a/package.json b/package.json index ce9a3781..fa32a4a3 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "format:documentation:check": "markdownlint-cli2 \"**/*.md\" \"#node_modules\"", "test": "jest", "validate:agents": "node scripts/validate-json-schema.js protocol/agents.json", + "validate:errors": "node errors/scripts/validate-errors.js", "validate:json-schema": "node scripts/validate-json-schema.js", "fetch:agent-releases": "node scripts/fetch-agent-releases.js", "export:agents": "node scripts/export-agents-csv.js", From eb0f1769d9fb9e2acafd2cf4be3387b2fbdc2ab1 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 11:29:42 +0100 Subject: [PATCH 4/9] Add stable identifier to every error code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an `identifier` frontmatter field to each codes/.md: a stable, unique snake_case name that is the canonical basis for the constant each SDK generates for the code. Unlike the title (prose that may be reworded), the identifier is a frozen contract, so SDKs can generate consistent constants from the registry rather than deriving churny names from description text. Seeded from existing SDK/server constants (ably-go, then realtime-go), falling back to the title, then normalised — abbreviations expanded, UK spelling, named after the cause rather than the consequence. The validator now requires `identifier`, checks it matches ^[a-z][a-z0-9_]*$, and rejects duplicates. README, guidelines and CLAUDE document the field. Co-Authored-By: Claude Opus 4.8 (1M context) --- errors/CLAUDE.md | 3 ++- errors/README.md | 4 +++- errors/codes/10000.md | 1 + errors/codes/101000.md | 1 + errors/codes/101001.md | 1 + errors/codes/101002.md | 1 + errors/codes/101003.md | 1 + errors/codes/101004.md | 1 + errors/codes/102000.md | 1 + errors/codes/102001.md | 1 + errors/codes/102002.md | 1 + errors/codes/102003.md | 1 + errors/codes/102004.md | 1 + errors/codes/102005.md | 1 + errors/codes/102050.md | 1 + errors/codes/102051.md | 1 + errors/codes/102052.md | 1 + errors/codes/102053.md | 1 + errors/codes/102054.md | 1 + errors/codes/102100.md | 1 + errors/codes/102101.md | 1 + errors/codes/102102.md | 1 + errors/codes/102103.md | 1 + errors/codes/102104.md | 1 + errors/codes/102105.md | 1 + errors/codes/102106.md | 1 + errors/codes/102107.md | 1 + errors/codes/102108.md | 1 + errors/codes/102109.md | 1 + errors/codes/102110.md | 1 + errors/codes/102111.md | 1 + errors/codes/102112.md | 1 + errors/codes/102113.md | 1 + errors/codes/102200.md | 1 + errors/codes/102201.md | 1 + errors/codes/102202.md | 1 + errors/codes/103000.md | 1 + errors/codes/103001.md | 1 + errors/codes/103002.md | 1 + errors/codes/103003.md | 1 + errors/codes/103004.md | 1 + errors/codes/103005.md | 1 + errors/codes/103006.md | 1 + errors/codes/103007.md | 1 + errors/codes/103008.md | 1 + errors/codes/20000.md | 1 + errors/codes/40000.md | 1 + errors/codes/40001.md | 1 + errors/codes/40002.md | 1 + errors/codes/40003.md | 1 + errors/codes/40004.md | 1 + errors/codes/40005.md | 1 + errors/codes/40006.md | 1 + errors/codes/40007.md | 1 + errors/codes/40008.md | 1 + errors/codes/40009.md | 1 + errors/codes/40010.md | 1 + errors/codes/40011.md | 1 + errors/codes/40012.md | 1 + errors/codes/40013.md | 1 + errors/codes/40014.md | 1 + errors/codes/40015.md | 1 + errors/codes/40016.md | 1 + errors/codes/40017.md | 1 + errors/codes/40018.md | 1 + errors/codes/40019.md | 1 + errors/codes/40020.md | 1 + errors/codes/40021.md | 1 + errors/codes/40022.md | 1 + errors/codes/40023.md | 1 + errors/codes/40024.md | 1 + errors/codes/40025.md | 1 + errors/codes/40030.md | 1 + errors/codes/40031.md | 1 + errors/codes/40032.md | 1 + errors/codes/40033.md | 1 + errors/codes/40034.md | 1 + errors/codes/40035.md | 1 + errors/codes/40099.md | 1 + errors/codes/40100.md | 1 + errors/codes/40101.md | 1 + errors/codes/40102.md | 1 + errors/codes/40103.md | 1 + errors/codes/40104.md | 1 + errors/codes/40105.md | 1 + errors/codes/40106.md | 1 + errors/codes/40110.md | 1 + errors/codes/40111.md | 1 + errors/codes/40112.md | 1 + errors/codes/40113.md | 1 + errors/codes/40114.md | 1 + errors/codes/40115.md | 1 + errors/codes/40120.md | 1 + errors/codes/40121.md | 1 + errors/codes/40125.md | 1 + errors/codes/40126.md | 1 + errors/codes/40127.md | 1 + errors/codes/40130.md | 1 + errors/codes/40131.md | 1 + errors/codes/40132.md | 1 + errors/codes/40133.md | 1 + errors/codes/40140.md | 1 + errors/codes/40141.md | 1 + errors/codes/40142.md | 1 + errors/codes/40143.md | 1 + errors/codes/40144.md | 1 + errors/codes/40145.md | 1 + errors/codes/40150.md | 1 + errors/codes/40160.md | 1 + errors/codes/40161.md | 1 + errors/codes/40162.md | 1 + errors/codes/40163.md | 1 + errors/codes/40164.md | 1 + errors/codes/40165.md | 1 + errors/codes/40166.md | 1 + errors/codes/40170.md | 1 + errors/codes/40171.md | 1 + errors/codes/40172.md | 1 + errors/codes/40180.md | 1 + errors/codes/40181.md | 1 + errors/codes/40300.md | 1 + errors/codes/40310.md | 1 + errors/codes/40311.md | 1 + errors/codes/40320.md | 1 + errors/codes/40330.md | 1 + errors/codes/40331.md | 1 + errors/codes/40332.md | 1 + errors/codes/40400.md | 1 + errors/codes/40500.md | 1 + errors/codes/40900.md | 1 + errors/codes/41001.md | 1 + errors/codes/42200.md | 1 + errors/codes/42210.md | 1 + errors/codes/42211.md | 1 + errors/codes/42212.md | 1 + errors/codes/42213.md | 1 + errors/codes/42910.md | 1 + errors/codes/42911.md | 1 + errors/codes/42912.md | 1 + errors/codes/42913.md | 1 + errors/codes/42914.md | 1 + errors/codes/42915.md | 1 + errors/codes/42916.md | 1 + errors/codes/42917.md | 1 + errors/codes/42918.md | 1 + errors/codes/42920.md | 1 + errors/codes/42921.md | 1 + errors/codes/42922.md | 1 + errors/codes/42923.md | 1 + errors/codes/42924.md | 1 + errors/codes/42925.md | 1 + errors/codes/42926.md | 1 + errors/codes/50000.md | 1 + errors/codes/50001.md | 1 + errors/codes/50002.md | 1 + errors/codes/50003.md | 1 + errors/codes/50004.md | 1 + errors/codes/50005.md | 1 + errors/codes/50006.md | 1 + errors/codes/50010.md | 1 + errors/codes/50210.md | 1 + errors/codes/50310.md | 1 + errors/codes/50320.md | 1 + errors/codes/50330.md | 1 + errors/codes/50410.md | 1 + errors/codes/70000.md | 1 + errors/codes/70001.md | 1 + errors/codes/70002.md | 1 + errors/codes/70003.md | 1 + errors/codes/70004.md | 1 + errors/codes/70005.md | 1 + errors/codes/70006.md | 1 + errors/codes/71000.md | 1 + errors/codes/71001.md | 1 + errors/codes/71100.md | 1 + errors/codes/71101.md | 1 + errors/codes/71102.md | 1 + errors/codes/71200.md | 1 + errors/codes/71201.md | 1 + errors/codes/71202.md | 1 + errors/codes/71203.md | 1 + errors/codes/71204.md | 1 + errors/codes/71300.md | 1 + errors/codes/71301.md | 1 + errors/codes/71302.md | 1 + errors/codes/71303.md | 1 + errors/codes/72000.md | 1 + errors/codes/72001.md | 1 + errors/codes/72002.md | 1 + errors/codes/72003.md | 1 + errors/codes/72004.md | 1 + errors/codes/72005.md | 1 + errors/codes/72006.md | 1 + errors/codes/72007.md | 1 + errors/codes/72008.md | 1 + errors/codes/80000.md | 1 + errors/codes/80001.md | 1 + errors/codes/80002.md | 1 + errors/codes/80003.md | 1 + errors/codes/80004.md | 1 + errors/codes/80005.md | 1 + errors/codes/80006.md | 1 + errors/codes/80007.md | 1 + errors/codes/80008.md | 1 + errors/codes/80009.md | 1 + errors/codes/80010.md | 1 + errors/codes/80011.md | 1 + errors/codes/80012.md | 1 + errors/codes/80013.md | 1 + errors/codes/80014.md | 1 + errors/codes/80015.md | 1 + errors/codes/80016.md | 1 + errors/codes/80017.md | 1 + errors/codes/80018.md | 1 + errors/codes/80019.md | 1 + errors/codes/80020.md | 1 + errors/codes/80021.md | 1 + errors/codes/80022.md | 1 + errors/codes/80023.md | 1 + errors/codes/80024.md | 1 + errors/codes/80030.md | 1 + errors/codes/90000.md | 1 + errors/codes/90001.md | 1 + errors/codes/90002.md | 1 + errors/codes/90003.md | 1 + errors/codes/90004.md | 1 + errors/codes/90005.md | 1 + errors/codes/90006.md | 1 + errors/codes/90007.md | 1 + errors/codes/90008.md | 1 + errors/codes/90009.md | 1 + errors/codes/90010.md | 1 + errors/codes/90021.md | 1 + errors/codes/91000.md | 1 + errors/codes/91001.md | 1 + errors/codes/91002.md | 1 + errors/codes/91003.md | 1 + errors/codes/91004.md | 1 + errors/codes/91005.md | 1 + errors/codes/91006.md | 1 + errors/codes/91007.md | 1 + errors/codes/91008.md | 1 + errors/codes/91100.md | 1 + errors/codes/92000.md | 1 + errors/codes/92001.md | 1 + errors/codes/92002.md | 1 + errors/codes/92003.md | 1 + errors/codes/92004.md | 1 + errors/codes/92005.md | 1 + errors/codes/92006.md | 1 + errors/codes/92007.md | 1 + errors/codes/92008.md | 1 + errors/codes/93001.md | 1 + errors/codes/93002.md | 1 + errors/guidelines.md | 12 ++++++++++++ errors/scripts/validate-errors.js | 22 ++++++++++++++++++---- 256 files changed, 287 insertions(+), 6 deletions(-) diff --git a/errors/CLAUDE.md b/errors/CLAUDE.md index 51e88920..0bee91b8 100644 --- a/errors/CLAUDE.md +++ b/errors/CLAUDE.md @@ -4,7 +4,7 @@ ## What this directory holds -The per-code files live in [`codes/`](./codes): one Markdown file per error code, named `.md` (e.g. `40142.md`). Each file has YAML frontmatter (`code`, `title`, `summary`) and an optional Markdown body that provides the detail-page content beneath the title and summary. Title, summary, and body conventions are all covered in `guidelines.md`. +The per-code files live in [`codes/`](./codes): one Markdown file per error code, named `.md` (e.g. `40142.md`). Each file has YAML frontmatter (`code`, `identifier`, `title`, `summary`) and an optional Markdown body that provides the detail-page content beneath the title and summary. Title, summary, and body conventions are all covered in `guidelines.md`. The set of files in `codes/` **is the registry** — the source of truth for which codes are valid and what they mean. `protocol/errors.json` is the legacy `code → title` map this registry supersedes; it is being retired. While it still exists, the validator (`scripts/`) checks that every `errors.json` code has a matching `codes/.md`, so keep the two aligned when adding a code. @@ -13,6 +13,7 @@ The set of files in `codes/` **is the registry** — the source of truth for whi These are reminders, not substitutes for `guidelines.md`. - **Filename = code.** `40142.md` must have `code: 40142` in frontmatter. +- **Identifier** — a stable `snake_case` name (`^[a-z][a-z0-9_]*$`), unique across the registry, the canonical basis for each SDK's generated constant. Name the cause not the consequence; concise but not cryptic; UK spelling. A frozen contract — don't churn it, and don't derive it mechanically from the title. - **Title** — 3–7 words (cap 10), short phrase or concise subject–verb clause (not necessarily a noun phrase), sentence case, no trailing punctuation, no error-code restatement. Specific enough that a reader can distinguish it from neighbouring errors at a glance. - **Summary** — 15–40 words, one or two sentences, plain language. Describe what happened, then (when known) why it might have happened. Don't prescribe remediation — no "Check…", "Verify…", "Retry…". Generic phrasing — the summary represents *every* occurrence of the code, so avoid deictic ("this token", "your channel"); prefer "the token", "the channel". No Markdown formatting, stack traces, or internal identifiers. - **Body** (optional) — detail-page content beneath the title and summary. Don't restate either. Order: *What you should do* (lead with the triage — including when the answer is "nothing", for transient/self-healing errors) → *Why it happens* (causes in the reader's terms, each with its fix) → *What you'll see* (message string(s) and status, last, as a findability aid). Link out to feature docs for how-to; no inline code samples. No "related errors" section — disambiguate inline only when codes are genuinely confusable. Describe generically, instruct in second person. diff --git a/errors/README.md b/errors/README.md index 0c4aaad8..08f1fb13 100644 --- a/errors/README.md +++ b/errors/README.md @@ -4,17 +4,19 @@ This directory is the canonical registry of Ably's error codes and their custome ## Registering a new error code -To register a new code, create a Markdown file at `codes/.md` (the filename must be the numeric code, e.g. `codes/40010.md`). It must start with YAML frontmatter containing three required fields: +To register a new code, create a Markdown file at `codes/.md` (the filename must be the numeric code, e.g. `codes/40010.md`). It must start with YAML frontmatter containing four required fields: ```markdown --- code: 40010 +identifier: invalid_channel_name title: Invalid channel name summary: The channel name in the request was not valid, for example because it was empty, contained characters that are not permitted, or used an unknown square-bracketed prefix. --- ``` - **`code`** — the numeric error code. Must match the filename. +- **`identifier`** — a stable, unique `snake_case` name for the code, e.g. `invalid_channel_name`. - **`title`** — a short phrase identifying the error at a glance (aim for 3–7 words), e.g. `Invalid channel name`. - **`summary`** — one or two plain-language sentences (roughly 15–40 words) covering what happened and, when known, why. diff --git a/errors/codes/10000.md b/errors/codes/10000.md index fe98bbe0..e7a39519 100644 --- a/errors/codes/10000.md +++ b/errors/codes/10000.md @@ -1,5 +1,6 @@ --- code: 10000 +identifier: no_error title: No error summary: A placeholder code indicating success, used where an error code is expected but the operation completed normally. It does not represent a failure. --- diff --git a/errors/codes/101000.md b/errors/codes/101000.md index bc109cf9..994b30be 100644 --- a/errors/codes/101000.md +++ b/errors/codes/101000.md @@ -1,5 +1,6 @@ --- code: 101000 +identifier: space_name_is_empty title: Space name is empty summary: A space could not be created or referenced because no name was supplied. --- diff --git a/errors/codes/101001.md b/errors/codes/101001.md index f3400f93..91f74fd3 100644 --- a/errors/codes/101001.md +++ b/errors/codes/101001.md @@ -1,5 +1,6 @@ --- code: 101001 +identifier: space_must_be_entered_first title: Space must be entered first summary: An operation was attempted that requires having first entered the space. In Ably Spaces, actions such as updating a member's location or profile are only available once the space has been entered. --- diff --git a/errors/codes/101002.md b/errors/codes/101002.md index e3b44dc5..b8e601b4 100644 --- a/errors/codes/101002.md +++ b/errors/codes/101002.md @@ -1,5 +1,6 @@ --- code: 101002 +identifier: lock_request_already_pending title: Lock request already pending summary: A lock could not be requested because a request for the same lock is already in progress. In Ably Spaces, a member may only have one outstanding request for a given lock at a time. --- diff --git a/errors/codes/101003.md b/errors/codes/101003.md index e4e3a189..35d21aff 100644 --- a/errors/codes/101003.md +++ b/errors/codes/101003.md @@ -1,5 +1,6 @@ --- code: 101003 +identifier: lock_already_held title: Lock already held summary: A lock could not be acquired because it is currently held by another member. In Ably Spaces, only one member can hold a given lock at a time, and it must be released before another can take it. --- diff --git a/errors/codes/101004.md b/errors/codes/101004.md index f1c951cc..0ef3d55c 100644 --- a/errors/codes/101004.md +++ b/errors/codes/101004.md @@ -1,5 +1,6 @@ --- code: 101004 +identifier: lock_invalidated_by_concurrent_request title: Lock invalidated by concurrent request summary: A lock that appeared to be acquired was invalidated because another member requested the same lock concurrently and now holds it. --- diff --git a/errors/codes/102000.md b/errors/codes/102000.md index df9586ca..ea014fd2 100644 --- a/errors/codes/102000.md +++ b/errors/codes/102000.md @@ -1,5 +1,6 @@ --- code: 102000 +identifier: invalid_chat_room_name title: Invalid chat room name summary: A Chat room could not be used because the name supplied for it was not valid. --- diff --git a/errors/codes/102001.md b/errors/codes/102001.md index ecf6c967..3a9270e4 100644 --- a/errors/codes/102001.md +++ b/errors/codes/102001.md @@ -1,5 +1,6 @@ --- code: 102001 +identifier: room_attach_failed_for_messages title: Room attach failed for messages summary: Attaching a Chat room failed because its messages feature could not be attached. --- diff --git a/errors/codes/102002.md b/errors/codes/102002.md index 919bb942..cc1a9ca3 100644 --- a/errors/codes/102002.md +++ b/errors/codes/102002.md @@ -1,5 +1,6 @@ --- code: 102002 +identifier: room_attach_failed_for_presence title: Room attach failed for presence summary: Attaching a Chat room failed because its presence feature could not be attached. --- diff --git a/errors/codes/102003.md b/errors/codes/102003.md index 1d358034..2872d6e6 100644 --- a/errors/codes/102003.md +++ b/errors/codes/102003.md @@ -1,5 +1,6 @@ --- code: 102003 +identifier: room_attach_failed_for_reactions title: Room attach failed for reactions summary: Attaching a Chat room failed because its reactions feature could not be attached. --- diff --git a/errors/codes/102004.md b/errors/codes/102004.md index 1fe75d17..e62f9c5a 100644 --- a/errors/codes/102004.md +++ b/errors/codes/102004.md @@ -1,5 +1,6 @@ --- code: 102004 +identifier: room_attach_failed_for_occupancy title: Room attach failed for occupancy summary: Attaching a Chat room failed because its occupancy feature could not be attached. --- diff --git a/errors/codes/102005.md b/errors/codes/102005.md index 9c4da8fc..3544e31a 100644 --- a/errors/codes/102005.md +++ b/errors/codes/102005.md @@ -1,5 +1,6 @@ --- code: 102005 +identifier: room_attach_failed_for_typing title: Room attach failed for typing summary: Attaching a Chat room failed because its typing feature could not be attached. --- diff --git a/errors/codes/102050.md b/errors/codes/102050.md index 3b8eaab5..e7296eee 100644 --- a/errors/codes/102050.md +++ b/errors/codes/102050.md @@ -1,5 +1,6 @@ --- code: 102050 +identifier: room_detach_failed_for_messages title: Room detach failed for messages summary: Detaching a Chat room failed because its messages feature could not be detached. --- diff --git a/errors/codes/102051.md b/errors/codes/102051.md index 36eb3704..50941b43 100644 --- a/errors/codes/102051.md +++ b/errors/codes/102051.md @@ -1,5 +1,6 @@ --- code: 102051 +identifier: room_detach_failed_for_presence title: Room detach failed for presence summary: Detaching a Chat room failed because its presence feature could not be detached. --- diff --git a/errors/codes/102052.md b/errors/codes/102052.md index e414fc7d..8dc4f5b5 100644 --- a/errors/codes/102052.md +++ b/errors/codes/102052.md @@ -1,5 +1,6 @@ --- code: 102052 +identifier: room_detach_failed_for_reactions title: Room detach failed for reactions summary: Detaching a Chat room failed because its reactions feature could not be detached. --- diff --git a/errors/codes/102053.md b/errors/codes/102053.md index 152e690f..45edfa2e 100644 --- a/errors/codes/102053.md +++ b/errors/codes/102053.md @@ -1,5 +1,6 @@ --- code: 102053 +identifier: room_detach_failed_for_occupancy title: Room detach failed for occupancy summary: Detaching a Chat room failed because its occupancy feature could not be detached. --- diff --git a/errors/codes/102054.md b/errors/codes/102054.md index 4e6ef0f0..c66f12c3 100644 --- a/errors/codes/102054.md +++ b/errors/codes/102054.md @@ -1,5 +1,6 @@ --- code: 102054 +identifier: room_detach_failed_for_typing title: Room detach failed for typing summary: Detaching a Chat room failed because its typing feature could not be detached. --- diff --git a/errors/codes/102100.md b/errors/codes/102100.md index c6e88bfc..e16beb12 100644 --- a/errors/codes/102100.md +++ b/errors/codes/102100.md @@ -1,5 +1,6 @@ --- code: 102100 +identifier: room_continuity_not_guaranteed title: Room continuity not guaranteed summary: A discontinuity was detected on the Chat room, so the continuity of messages could not be guaranteed. This usually follows a disconnection long enough that the room could not resume from where it left off. --- diff --git a/errors/codes/102101.md b/errors/codes/102101.md index aeafad87..57b05bce 100644 --- a/errors/codes/102101.md +++ b/errors/codes/102101.md @@ -1,5 +1,6 @@ --- code: 102101 +identifier: room_is_in_the_failed_state title: Room is in the failed state summary: The operation could not be performed because the Chat room is in the failed state. A room enters this state when an earlier operation on it failed and could not recover. --- diff --git a/errors/codes/102102.md b/errors/codes/102102.md index fe9dac35..634a3b73 100644 --- a/errors/codes/102102.md +++ b/errors/codes/102102.md @@ -1,5 +1,6 @@ --- code: 102102 +identifier: room_is_being_released title: Room is being released summary: The operation could not be performed because the Chat room is in the process of being released. --- diff --git a/errors/codes/102103.md b/errors/codes/102103.md index 5d335c73..90e9313c 100644 --- a/errors/codes/102103.md +++ b/errors/codes/102103.md @@ -1,5 +1,6 @@ --- code: 102103 +identifier: room_has_been_released title: Room has been released summary: The operation could not be performed because the Chat room has already been released. --- diff --git a/errors/codes/102104.md b/errors/codes/102104.md index 8158f974..58247a42 100644 --- a/errors/codes/102104.md +++ b/errors/codes/102104.md @@ -1,5 +1,6 @@ --- code: 102104 +identifier: room_operation_failed_after_earlier_attempt title: Room operation failed after earlier attempt summary: The operation failed because a preceding attempt to perform it had already failed. --- diff --git a/errors/codes/102105.md b/errors/codes/102105.md index d27a9b8d..50f5980d 100644 --- a/errors/codes/102105.md +++ b/errors/codes/102105.md @@ -1,5 +1,6 @@ --- code: 102105 +identifier: unknown_room_lifecycle_error title: Unknown room lifecycle error summary: An unexpected error occurred in the Chat room lifecycle that does not correspond to a known condition. --- diff --git a/errors/codes/102106.md b/errors/codes/102106.md index f8c4b143..3b3caeb1 100644 --- a/errors/codes/102106.md +++ b/errors/codes/102106.md @@ -1,5 +1,6 @@ --- code: 102106 +identifier: room_released_before_operation_completed title: Room released before operation completed summary: The operation could not complete because the Chat room was released while it was still in progress. --- diff --git a/errors/codes/102107.md b/errors/codes/102107.md index df7126ab..f5a3294d 100644 --- a/errors/codes/102107.md +++ b/errors/codes/102107.md @@ -1,5 +1,6 @@ --- code: 102107 +identifier: room_already_exists_with_different_options title: Room already exists with different options summary: A Chat room with the same name already exists but was created with different options. A room must be released before it can be requested again with a different configuration. --- diff --git a/errors/codes/102108.md b/errors/codes/102108.md index 392deeea..6aac0ba8 100644 --- a/errors/codes/102108.md +++ b/errors/codes/102108.md @@ -1,5 +1,6 @@ --- code: 102108 +identifier: feature_not_enabled_in_room_options title: Feature not enabled in room options summary: The operation required a feature that was not enabled when the Chat room was created. Features such as presence or typing indicators must be enabled in the room options to be used. --- diff --git a/errors/codes/102109.md b/errors/codes/102109.md index 6727a471..0309fb85 100644 --- a/errors/codes/102109.md +++ b/errors/codes/102109.md @@ -1,5 +1,6 @@ --- code: 102109 +identifier: history_requested_for_an_unsubscribed_listener title: History requested for an unsubscribed listener summary: A request for the messages published before a listener's subscription point was rejected because that listener was not subscribed. This history is only available while the listener is subscribed to the room. --- diff --git a/errors/codes/102110.md b/errors/codes/102110.md index 67d04ed8..87d31ab0 100644 --- a/errors/codes/102110.md +++ b/errors/codes/102110.md @@ -1,5 +1,6 @@ --- code: 102110 +identifier: channel_serial_not_defined title: Channel serial not defined summary: A channel serial was expected but was not defined at the point it was needed. --- diff --git a/errors/codes/102111.md b/errors/codes/102111.md index 4a07cd41..d6102748 100644 --- a/errors/codes/102111.md +++ b/errors/codes/102111.md @@ -1,5 +1,6 @@ --- code: 102111 +identifier: channel_options_modified_after_request title: Channel options modified after request summary: Channel options could not be changed because the underlying channel had already been requested. --- diff --git a/errors/codes/102112.md b/errors/codes/102112.md index 02273a7e..5edbf83c 100644 --- a/errors/codes/102112.md +++ b/errors/codes/102112.md @@ -1,5 +1,6 @@ --- code: 102112 +identifier: room_is_in_an_invalid_state title: Room is in an invalid state summary: The operation could not be performed because the Chat room was in a state that does not permit it. --- diff --git a/errors/codes/102113.md b/errors/codes/102113.md index 07819ff5..0e0f3b08 100644 --- a/errors/codes/102113.md +++ b/errors/codes/102113.md @@ -1,5 +1,6 @@ --- code: 102113 +identifier: sequential_execution_could_not_be_enforced title: Sequential execution could not be enforced summary: The operation failed because its sequential execution could not be enforced. Chat room lifecycle operations are expected to run one at a time, and that ordering could not be guaranteed. --- diff --git a/errors/codes/102200.md b/errors/codes/102200.md index 8851a715..2d17fb57 100644 --- a/errors/codes/102200.md +++ b/errors/codes/102200.md @@ -1,5 +1,6 @@ --- code: 102200 +identifier: hook_used_outside_its_provider title: Hook used outside its provider summary: An Ably Chat React hook was used in a component that is not wrapped in the required provider. The hooks depend on context supplied by a provider higher in the component tree. --- diff --git a/errors/codes/102201.md b/errors/codes/102201.md index 4e544021..72479eb7 100644 --- a/errors/codes/102201.md +++ b/errors/codes/102201.md @@ -1,5 +1,6 @@ --- code: 102201 +identifier: component_unmounted_before_completion title: Component unmounted before completion summary: An operation started by the Ably Chat React hooks could not finish because the React component was unmounted while it was still in progress. --- diff --git a/errors/codes/102202.md b/errors/codes/102202.md index 99bdb40b..94552a59 100644 --- a/errors/codes/102202.md +++ b/errors/codes/102202.md @@ -1,5 +1,6 @@ --- code: 102202 +identifier: presence_data_could_not_be_fetched title: Presence data could not be fetched summary: The Ably Chat React hooks could not retrieve presence data after the maximum number of retries. This usually points to a persistent problem reaching presence rather than a transient one. --- diff --git a/errors/codes/103000.md b/errors/codes/103000.md index 6e3b857b..4be8d018 100644 --- a/errors/codes/103000.md +++ b/errors/codes/103000.md @@ -1,5 +1,6 @@ --- code: 103000 +identifier: unable_to_publish_push_notification title: Push notification internal error summary: The push notification was not delivered because of an unexpected error within the Ably platform during preparation, dispatch, or requeue. The cause is not related to the contents of the request or the device's registration. --- diff --git a/errors/codes/103001.md b/errors/codes/103001.md index 18865ebd..f2123005 100644 --- a/errors/codes/103001.md +++ b/errors/codes/103001.md @@ -1,5 +1,6 @@ --- code: 103001 +identifier: push_publish_retries_exhausted title: Push notification retry limit reached summary: The push notification was not delivered because the configured retry limit was reached after repeated unsuccessful delivery attempts. The underlying failures are typically transient — provider outages, rate limits, or network errors. --- diff --git a/errors/codes/103002.md b/errors/codes/103002.md index e4d2be00..4cd93243 100644 --- a/errors/codes/103002.md +++ b/errors/codes/103002.md @@ -1,5 +1,6 @@ --- code: 103002 +identifier: push_direct_publish_no_recipient title: Push notification recipient missing summary: The push notification was not delivered because the direct push request was submitted without a recipient device. Direct push targets a single device by ID and cannot be processed when no recipient is supplied. --- diff --git a/errors/codes/103003.md b/errors/codes/103003.md index 61f74f03..f67bb4d1 100644 --- a/errors/codes/103003.md +++ b/errors/codes/103003.md @@ -1,5 +1,6 @@ --- code: 103003 +identifier: push_cannot_handle_body title: Push notification body invalid summary: The push notification was not delivered because its body could not be processed. This typically indicates a malformed or unsupported message structure in the publish request. --- diff --git a/errors/codes/103004.md b/errors/codes/103004.md index c2d32381..41535513 100644 --- a/errors/codes/103004.md +++ b/errors/codes/103004.md @@ -1,5 +1,6 @@ --- code: 103004 +identifier: push_notification_rejected_by_provider title: Push notification rejected by provider summary: The push notification was not delivered because the provider — APNs, FCM, or WebPush — rejected the request due to a problem with the payload or delivery parameters. Typical causes include an oversized payload, a disallowed topic, or an unsupported field. --- diff --git a/errors/codes/103005.md b/errors/codes/103005.md index 8f2cec27..135e4a3f 100644 --- a/errors/codes/103005.md +++ b/errors/codes/103005.md @@ -1,5 +1,6 @@ --- code: 103005 +identifier: push_device_token_invalid title: Push device token invalid summary: The push notification was not delivered because the provider — APNs, FCM, or WebPush — rejected the device's push token as invalid for the configured application. The token may have been issued under different credentials or for a different application. --- diff --git a/errors/codes/103006.md b/errors/codes/103006.md index b3840b9a..d21cb6f1 100644 --- a/errors/codes/103006.md +++ b/errors/codes/103006.md @@ -1,5 +1,6 @@ --- code: 103006 +identifier: push_transport_not_configured title: Push transport not configured summary: The push notification was not delivered because the device's push notification provider — APNs, FCM, or WebPush — has no credentials configured on the application. No deliveries can be made through that provider until the credentials are present. --- diff --git a/errors/codes/103007.md b/errors/codes/103007.md index 6d4eed0b..5800717a 100644 --- a/errors/codes/103007.md +++ b/errors/codes/103007.md @@ -1,5 +1,6 @@ --- code: 103007 +identifier: push_notification_provider_unreachable title: Push notification provider unreachable summary: The push notification was not delivered because the provider — APNs, FCM, or WebPush — could not be reached, returned a server error, or sent a response that could not be processed. The condition is usually transient. --- diff --git a/errors/codes/103008.md b/errors/codes/103008.md index e7785676..df109414 100644 --- a/errors/codes/103008.md +++ b/errors/codes/103008.md @@ -1,5 +1,6 @@ --- code: 103008 +identifier: push_transport_credentials_invalid title: Push transport credentials invalid summary: A push notification could not be sent because the credentials configured for the target platform were rejected or had expired, such as an expired APNs certificate. --- diff --git a/errors/codes/20000.md b/errors/codes/20000.md index f102fb44..e8998364 100644 --- a/errors/codes/20000.md +++ b/errors/codes/20000.md @@ -1,5 +1,6 @@ --- code: 20000 +identifier: general_error title: General error summary: A generic error that does not correspond to a more specific code. It is used as a catch-all when the condition could not be classified more precisely. --- diff --git a/errors/codes/40000.md b/errors/codes/40000.md index 34da8c08..5b7470a5 100644 --- a/errors/codes/40000.md +++ b/errors/codes/40000.md @@ -1,5 +1,6 @@ --- code: 40000 +identifier: bad_request title: Bad request summary: The request was rejected because it was invalid and could not be processed. --- diff --git a/errors/codes/40001.md b/errors/codes/40001.md index 6a2d5510..b4a65982 100644 --- a/errors/codes/40001.md +++ b/errors/codes/40001.md @@ -1,5 +1,6 @@ --- code: 40001 +identifier: invalid_request_body title: Invalid request body summary: The body of the request could not be processed because it was invalid, missing required fields, or not in the expected format. --- diff --git a/errors/codes/40002.md b/errors/codes/40002.md index 2400e5ea..8ba66c91 100644 --- a/errors/codes/40002.md +++ b/errors/codes/40002.md @@ -1,5 +1,6 @@ --- code: 40002 +identifier: invalid_request_parameter_name title: Invalid request parameter name summary: The request included a parameter that is not recognised or not permitted for the operation being performed. --- diff --git a/errors/codes/40003.md b/errors/codes/40003.md index c07a0480..1dc5225c 100644 --- a/errors/codes/40003.md +++ b/errors/codes/40003.md @@ -1,5 +1,6 @@ --- code: 40003 +identifier: invalid_parameter_value title: Invalid request parameter value summary: A parameter in the request was invalid, such as a value of the wrong type, outside the allowed range, or otherwise unacceptable for that parameter. --- diff --git a/errors/codes/40004.md b/errors/codes/40004.md index 7899ceba..3a31aea0 100644 --- a/errors/codes/40004.md +++ b/errors/codes/40004.md @@ -1,5 +1,6 @@ --- code: 40004 +identifier: invalid_request_header title: Invalid request header summary: A header supplied with the request was missing, invalid, or held a value that was not accepted for the operation. --- diff --git a/errors/codes/40005.md b/errors/codes/40005.md index a3ae5bdb..5e1d7a86 100644 --- a/errors/codes/40005.md +++ b/errors/codes/40005.md @@ -1,5 +1,6 @@ --- code: 40005 +identifier: invalid_credential title: Invalid API key or token summary: An API key or token supplied with the request could not be read because it was not in the expected form. This is distinct from a key or token that is well-formed but unauthorised. --- diff --git a/errors/codes/40006.md b/errors/codes/40006.md index 7d8c2efe..c67ad198 100644 --- a/errors/codes/40006.md +++ b/errors/codes/40006.md @@ -1,5 +1,6 @@ --- code: 40006 +identifier: message_contains_invalid_connection_id title: Message contains invalid connection ID summary: A published message referenced a connection by an ID or key that was invalid or did not match, so the message was rejected. --- diff --git a/errors/codes/40007.md b/errors/codes/40007.md index 1dd603f2..441a970d 100644 --- a/errors/codes/40007.md +++ b/errors/codes/40007.md @@ -1,5 +1,6 @@ --- code: 40007 +identifier: invalid_connection_serial title: Invalid connection serial summary: An older Ably SDK attempted to resume a connection using an invalid connection serial. --- diff --git a/errors/codes/40008.md b/errors/codes/40008.md index 531a0f3d..c177b74a 100644 --- a/errors/codes/40008.md +++ b/errors/codes/40008.md @@ -1,5 +1,6 @@ --- code: 40008 +identifier: invalid_content_length title: Invalid content length summary: More data arrived in the request body than its Content-Length header declared, so the request was rejected. --- diff --git a/errors/codes/40009.md b/errors/codes/40009.md index 9e8efc78..c171a74c 100644 --- a/errors/codes/40009.md +++ b/errors/codes/40009.md @@ -1,5 +1,6 @@ --- code: 40009 +identifier: max_message_size_exceeded title: Message too large summary: A message was rejected because it exceeded the maximum permitted size. When several messages are published in a single call, the limit applies to their combined size rather than to each one individually. --- diff --git a/errors/codes/40010.md b/errors/codes/40010.md index 842675dd..7da1d08f 100644 --- a/errors/codes/40010.md +++ b/errors/codes/40010.md @@ -1,5 +1,6 @@ --- code: 40010 +identifier: invalid_channel_name title: Invalid channel name summary: The channel name in the request was not valid, for example because it was empty, contained characters that are not permitted, or used an unknown square-bracketed prefix. --- diff --git a/errors/codes/40011.md b/errors/codes/40011.md index dd022a47..947f10d7 100644 --- a/errors/codes/40011.md +++ b/errors/codes/40011.md @@ -1,5 +1,6 @@ --- code: 40011 +identifier: pagination_sequence_no_longer_valid title: Pagination sequence no longer valid summary: An enumeration query could not continue because the underlying set of results shifted between pages, so the pagination sequence was no longer valid. --- diff --git a/errors/codes/40012.md b/errors/codes/40012.md index b5f80824..aa5cb668 100644 --- a/errors/codes/40012.md +++ b/errors/codes/40012.md @@ -1,5 +1,6 @@ --- code: 40012 +identifier: invalid_client_id title: Invalid client ID summary: The client ID supplied was not acceptable, for example because it was empty, a wildcard, not a string, or did not match the client ID permitted by the credentials in use. --- diff --git a/errors/codes/40013.md b/errors/codes/40013.md index 49752ade..4c64cc7d 100644 --- a/errors/codes/40013.md +++ b/errors/codes/40013.md @@ -1,5 +1,6 @@ --- code: 40013 +identifier: invalid_data_or_encoding title: Invalid message data or encoding summary: A message was rejected because its data was of an unsupported type, or because the encoding declared for the data could not be applied or reversed. --- diff --git a/errors/codes/40014.md b/errors/codes/40014.md index f8fe84c5..e421f275 100644 --- a/errors/codes/40014.md +++ b/errors/codes/40014.md @@ -1,5 +1,6 @@ --- code: 40014 +identifier: resource_disposed title: Resource disposed summary: The operation could not complete because an internal Ably resource it targeted was temporarily unavailable, typically during routine changes on Ably's side such as failover or rebalancing. --- diff --git a/errors/codes/40015.md b/errors/codes/40015.md index be492db1..060cf8cf 100644 --- a/errors/codes/40015.md +++ b/errors/codes/40015.md @@ -1,5 +1,6 @@ --- code: 40015 +identifier: invalid_device_id title: Invalid device ID summary: The device ID supplied for a push notification operation was missing or not in an acceptable form, so the request was rejected. --- diff --git a/errors/codes/40016.md b/errors/codes/40016.md index 76250410..930993f8 100644 --- a/errors/codes/40016.md +++ b/errors/codes/40016.md @@ -1,5 +1,6 @@ --- code: 40016 +identifier: invalid_message_name title: Invalid message name summary: A message supplied a name that was invalid, so the message was rejected. --- diff --git a/errors/codes/40017.md b/errors/codes/40017.md index 8f3100e1..82d7dd16 100644 --- a/errors/codes/40017.md +++ b/errors/codes/40017.md @@ -1,5 +1,6 @@ --- code: 40017 +identifier: unsupported_protocol_version title: Unsupported protocol version summary: The request specified a protocol version that Ably does not support, or omitted a version where one is required. The connection or request was rejected before processing. --- diff --git a/errors/codes/40018.md b/errors/codes/40018.md index 548f004d..4fa58ec2 100644 --- a/errors/codes/40018.md +++ b/errors/codes/40018.md @@ -1,5 +1,6 @@ --- code: 40018 +identifier: delta_decoding_failed title: Message delta could not be applied summary: On a channel using delta compression, the Ably SDK could not apply a message's delta against the preceding message — for example because that message was missed or arrived out of order. The SDK recovers automatically by reattaching the channel to receive a full message. --- diff --git a/errors/codes/40019.md b/errors/codes/40019.md index 989851e2..ea41e0dd 100644 --- a/errors/codes/40019.md +++ b/errors/codes/40019.md @@ -1,5 +1,6 @@ --- code: 40019 +identifier: missing_plugin title: Required SDK plugin not present summary: The operation needed an optional Ably SDK plugin that was not installed or registered, so it could not be carried out. Some features are provided by plugins that must be added alongside the core SDK. --- diff --git a/errors/codes/40020.md b/errors/codes/40020.md index 3245d81a..e4e2cc80 100644 --- a/errors/codes/40020.md +++ b/errors/codes/40020.md @@ -1,5 +1,6 @@ --- code: 40020 +identifier: batch_request_error title: Batch request error summary: A batch request completed with one or more of its items failing. Each failing item carries its own error, reported alongside this one. --- diff --git a/errors/codes/40021.md b/errors/codes/40021.md index f5334c2e..5a2a07d4 100644 --- a/errors/codes/40021.md +++ b/errors/codes/40021.md @@ -1,5 +1,6 @@ --- code: 40021 +identifier: feature_requires_a_newer_platform_version title: Feature requires a newer platform version summary: The requested feature is not available on the platform version in use. It relies on capabilities added in a later version than the one handling the request. --- diff --git a/errors/codes/40022.md b/errors/codes/40022.md index 2edf052b..3d2cf096 100644 --- a/errors/codes/40022.md +++ b/errors/codes/40022.md @@ -1,5 +1,6 @@ --- code: 40022 +identifier: api_streamer_no_longer_offered title: API Streamer no longer offered summary: The request targeted API Streamer, which has been shut down and is no longer available. Requests that depend on it can no longer be served. --- diff --git a/errors/codes/40023.md b/errors/codes/40023.md index 4f93f1a1..846ef15d 100644 --- a/errors/codes/40023.md +++ b/errors/codes/40023.md @@ -1,5 +1,6 @@ --- code: 40023 +identifier: operation_requires_a_newer_sdk_version title: Operation requires a newer SDK version summary: The requested operation is not supported by the Ably SDK version in use, because it relies on a capability available only in a more recent version. --- diff --git a/errors/codes/40024.md b/errors/codes/40024.md index 0a227fb8..3fcf15b7 100644 --- a/errors/codes/40024.md +++ b/errors/codes/40024.md @@ -1,5 +1,6 @@ --- code: 40024 +identifier: incompatible_site_for_history title: untilAttach history request not served by attach region summary: A history request using untilAttach was handled by a different Ably region from the one the channel attached in. The attach point is specific to that region, so the request could not be served and no messages were returned. --- diff --git a/errors/codes/40025.md b/errors/codes/40025.md index 4535718e..0360cfc0 100644 --- a/errors/codes/40025.md +++ b/errors/codes/40025.md @@ -1,5 +1,6 @@ --- code: 40025 +identifier: api_no_longer_supported title: API no longer supported summary: A method or feature that Ably no longer supports was called. It has been removed or replaced by a newer API, so the call is rejected instead of being performed. --- diff --git a/errors/codes/40030.md b/errors/codes/40030.md index 43e9c87b..4d664ba5 100644 --- a/errors/codes/40030.md +++ b/errors/codes/40030.md @@ -1,5 +1,6 @@ --- code: 40030 +identifier: invalid_publish_request title: Invalid publish request summary: A publish request was rejected because it was invalid. --- diff --git a/errors/codes/40031.md b/errors/codes/40031.md index 6f8838cc..47466643 100644 --- a/errors/codes/40031.md +++ b/errors/codes/40031.md @@ -1,5 +1,6 @@ --- code: 40031 +identifier: invalid_client_specified_message_id title: Invalid client-specified message id summary: A publish was rejected because a client-supplied message id was missing, empty, or not in the required format. When several messages are published together, every message must carry a valid id following the expected pattern. --- diff --git a/errors/codes/40032.md b/errors/codes/40032.md index de7ec81f..4eea5f08 100644 --- a/errors/codes/40032.md +++ b/errors/codes/40032.md @@ -1,5 +1,6 @@ --- code: 40032 +identifier: invalid_message_extras_field title: Invalid message extras field summary: A publish was rejected because a message included an extras field that is not permitted, or one whose value was the wrong type. Only recognised extras keys with values of the expected shape are accepted. --- diff --git a/errors/codes/40033.md b/errors/codes/40033.md index fcee333b..faef947e 100644 --- a/errors/codes/40033.md +++ b/errors/codes/40033.md @@ -1,5 +1,6 @@ --- code: 40033 +identifier: operation_cancelled title: Operation cancelled summary: An operation was stopped before completing, so its result was discarded. This usually happens when the connection or request that triggered the operation ends first. --- diff --git a/errors/codes/40034.md b/errors/codes/40034.md index 039b773e..014415c9 100644 --- a/errors/codes/40034.md +++ b/errors/codes/40034.md @@ -1,5 +1,6 @@ --- code: 40034 +identifier: too_many_messages_in_one_publish title: Too many messages in one publish summary: A publish was rejected because it contained more messages than the maximum permitted count. This is about the number of messages, not their combined size, which is limited separately. --- diff --git a/errors/codes/40035.md b/errors/codes/40035.md index a78b64ff..e8297363 100644 --- a/errors/codes/40035.md +++ b/errors/codes/40035.md @@ -1,5 +1,6 @@ --- code: 40035 +identifier: integration_message_filter_regex_not_re2_compatible title: Integration message filter regex not RE2-compatible summary: An integration's message filter could not be applied because its regular expression is not compatible with the RE2 syntax. Backreferences and lookaround are not supported. --- diff --git a/errors/codes/40099.md b/errors/codes/40099.md index 5d061503..74de7111 100644 --- a/errors/codes/40099.md +++ b/errors/codes/40099.md @@ -1,5 +1,6 @@ --- code: 40099 +identifier: reserved_for_testing title: Reserved for testing summary: This code is reserved for artificial errors produced during testing and does not indicate a genuine fault. It is not expected to appear during normal operation. --- diff --git a/errors/codes/40100.md b/errors/codes/40100.md index 0b7ff298..76aec7ad 100644 --- a/errors/codes/40100.md +++ b/errors/codes/40100.md @@ -1,5 +1,6 @@ --- code: 40100 +identifier: unauthorised title: Unauthorized summary: A connection or request was rejected because it was not authorized. --- diff --git a/errors/codes/40101.md b/errors/codes/40101.md index bf549de2..3731a293 100644 --- a/errors/codes/40101.md +++ b/errors/codes/40101.md @@ -1,5 +1,6 @@ --- code: 40101 +identifier: invalid_credentials title: Authentication failed summary: The credentials presented were not accepted — for example an API key secret that did not match, an invalid token, or no credentials supplied at all. --- diff --git a/errors/codes/40102.md b/errors/codes/40102.md index be1307d9..858f52e3 100644 --- a/errors/codes/40102.md +++ b/errors/codes/40102.md @@ -1,5 +1,6 @@ --- code: 40102 +identifier: incompatible_credentials title: Incompatible credentials summary: The credentials presented were valid but did not match the request — for example the client ID they permit differed from the one in use, or they belonged to a different application than the connection being resumed. --- diff --git a/errors/codes/40103.md b/errors/codes/40103.md index cb990f28..efd3f99a 100644 --- a/errors/codes/40103.md +++ b/errors/codes/40103.md @@ -1,5 +1,6 @@ --- code: 40103 +identifier: invalid_use_of_basic_auth_over_non_tls_transport title: Basic authentication used over an insecure connection summary: Basic authentication was attempted over a connection that was not secured with TLS. Basic authentication sends the API key directly, so it is only permitted over an encrypted transport. --- diff --git a/errors/codes/40104.md b/errors/codes/40104.md index 8c34e42f..7d646820 100644 --- a/errors/codes/40104.md +++ b/errors/codes/40104.md @@ -1,5 +1,6 @@ --- code: 40104 +identifier: token_request_timestamp_outside_permitted_window title: Token request timestamp outside permitted window summary: The timestamp in a token request fell outside the window Ably accepts, so the request was rejected. This usually happens when the clock of the system that generated the token request differs significantly from real time. --- diff --git a/errors/codes/40105.md b/errors/codes/40105.md index b8400745..a6fd2bd6 100644 --- a/errors/codes/40105.md +++ b/errors/codes/40105.md @@ -1,5 +1,6 @@ --- code: 40105 +identifier: nonce_value_replayed title: Nonce value replayed summary: A token request was rejected because its nonce had already been used. Each nonce may be presented only once, so a repeated value is treated as a replayed request. --- diff --git a/errors/codes/40106.md b/errors/codes/40106.md index 12732e9f..5e695f23 100644 --- a/errors/codes/40106.md +++ b/errors/codes/40106.md @@ -1,5 +1,6 @@ --- code: 40106 +identifier: no_valid_authentication_method_provided title: No valid authentication method provided summary: The Ably SDK was given no usable way to authenticate — no API key, token, or token-request mechanism such as authCallback or authUrl was provided. --- diff --git a/errors/codes/40110.md b/errors/codes/40110.md index c1edfbbd..49b34add 100644 --- a/errors/codes/40110.md +++ b/errors/codes/40110.md @@ -1,5 +1,6 @@ --- code: 40110 +identifier: account_disabled title: Account disabled summary: The request was rejected because the Ably account it belongs to is disabled. While an account is disabled, its applications cannot authenticate or carry traffic. --- diff --git a/errors/codes/40111.md b/errors/codes/40111.md index 5c67a081..794c6bbd 100644 --- a/errors/codes/40111.md +++ b/errors/codes/40111.md @@ -1,5 +1,6 @@ --- code: 40111 +identifier: account_connection_limit_exceeded title: Account connection limit exceeded summary: A new connection was refused because the account had reached the maximum number of concurrent connections permitted by its limits. --- diff --git a/errors/codes/40112.md b/errors/codes/40112.md index efd99d74..cb378519 100644 --- a/errors/codes/40112.md +++ b/errors/codes/40112.md @@ -1,5 +1,6 @@ --- code: 40112 +identifier: account_message_limit_exceeded title: Account message limit exceeded summary: The request was rejected because the account had reached the maximum message volume permitted by its limits. --- diff --git a/errors/codes/40113.md b/errors/codes/40113.md index a0e5a267..459bebed 100644 --- a/errors/codes/40113.md +++ b/errors/codes/40113.md @@ -1,5 +1,6 @@ --- code: 40113 +identifier: account_blocked title: Account blocked summary: The request was rejected because the Ably account it belongs to is blocked. --- diff --git a/errors/codes/40114.md b/errors/codes/40114.md index 67256f06..17a1afa0 100644 --- a/errors/codes/40114.md +++ b/errors/codes/40114.md @@ -1,5 +1,6 @@ --- code: 40114 +identifier: account_channel_limit_exceeded title: Account channel limit exceeded summary: The request was refused because the account had reached the maximum number of concurrent channels permitted by its limits. --- diff --git a/errors/codes/40115.md b/errors/codes/40115.md index 896c1c11..7f48810b 100644 --- a/errors/codes/40115.md +++ b/errors/codes/40115.md @@ -1,5 +1,6 @@ --- code: 40115 +identifier: account_request_limit_exceeded title: Account request limit exceeded summary: The request was refused because the account had reached its permitted limit on API and token requests. --- diff --git a/errors/codes/40120.md b/errors/codes/40120.md index 47a79c52..eb384efc 100644 --- a/errors/codes/40120.md +++ b/errors/codes/40120.md @@ -1,5 +1,6 @@ --- code: 40120 +identifier: application_disabled title: Application disabled summary: The request was rejected because the application it targets is disabled. While an application is disabled, it cannot authenticate or carry traffic. --- diff --git a/errors/codes/40121.md b/errors/codes/40121.md index 0ceb86ab..fca2d783 100644 --- a/errors/codes/40121.md +++ b/errors/codes/40121.md @@ -1,5 +1,6 @@ --- code: 40121 +identifier: token_revocation_not_enabled title: Token revocation not enabled summary: A token revocation request was rejected because the revocation capability it requires is not enabled — either token revocation for the application, or revocation by channel for the account. --- diff --git a/errors/codes/40125.md b/errors/codes/40125.md index 225355a3..40192c5d 100644 --- a/errors/codes/40125.md +++ b/errors/codes/40125.md @@ -1,5 +1,6 @@ --- code: 40125 +identifier: application_integration_limit_exceeded title: Application integration limit exceeded summary: An integration could not be created because the application had reached the maximum number of integrations permitted by its limits. --- diff --git a/errors/codes/40126.md b/errors/codes/40126.md index d5fb623b..cd608a86 100644 --- a/errors/codes/40126.md +++ b/errors/codes/40126.md @@ -1,5 +1,6 @@ --- code: 40126 +identifier: application_channel_rule_limit_exceeded title: Application channel rule limit exceeded summary: A channel rule could not be created because the application had reached the maximum number of channel rules permitted by its limits. --- diff --git a/errors/codes/40127.md b/errors/codes/40127.md index fb0ae04d..a4be1693 100644 --- a/errors/codes/40127.md +++ b/errors/codes/40127.md @@ -1,5 +1,6 @@ --- code: 40127 +identifier: application_api_key_limit_exceeded title: Application API key limit exceeded summary: A new API key could not be created because the application had reached the maximum number of keys permitted by its limits. --- diff --git a/errors/codes/40130.md b/errors/codes/40130.md index a5a6d21e..947ee9be 100644 --- a/errors/codes/40130.md +++ b/errors/codes/40130.md @@ -1,5 +1,6 @@ --- code: 40130 +identifier: key_error title: API key not recognised summary: Authentication failed because the API key presented was not recognised. This usually means the key does not exist or has been removed. --- diff --git a/errors/codes/40131.md b/errors/codes/40131.md index 4f976633..b40ac8f6 100644 --- a/errors/codes/40131.md +++ b/errors/codes/40131.md @@ -1,5 +1,6 @@ --- code: 40131 +identifier: key_revoked title: API key revoked summary: A connection or request was rejected because the API key it used has been revoked. A revoked key is permanently invalidated and can no longer authenticate. --- diff --git a/errors/codes/40132.md b/errors/codes/40132.md index e68d4cbb..8a59ae27 100644 --- a/errors/codes/40132.md +++ b/errors/codes/40132.md @@ -1,5 +1,6 @@ --- code: 40132 +identifier: key_expired title: API key expired summary: A connection or request was rejected because the API key it used has expired. Keys can be given an expiry time, after which they can no longer authenticate. --- diff --git a/errors/codes/40133.md b/errors/codes/40133.md index 64fcc4ac..72e95c2e 100644 --- a/errors/codes/40133.md +++ b/errors/codes/40133.md @@ -1,5 +1,6 @@ --- code: 40133 +identifier: wrong_api_key_for_token_revocation title: Wrong API key for token revocation summary: A token revocation request was rejected because it was made with a different API key from the one that issued the tokens. Tokens can only be revoked using the key that issued them. --- diff --git a/errors/codes/40140.md b/errors/codes/40140.md index f24347b7..952f8887 100644 --- a/errors/codes/40140.md +++ b/errors/codes/40140.md @@ -1,5 +1,6 @@ --- code: 40140 +identifier: token_error_unspecified title: Token not accepted summary: The authentication token was rejected, so the connection or request could not be authenticated. This error prompts the client to obtain a new token and retry. --- diff --git a/errors/codes/40141.md b/errors/codes/40141.md index eec0c090..4ec7d497 100644 --- a/errors/codes/40141.md +++ b/errors/codes/40141.md @@ -1,5 +1,6 @@ --- code: 40141 +identifier: token_revoked title: Token revoked summary: A connection or request was rejected because the authentication token it used has been revoked. A revoked token is invalidated before its normal expiry and can no longer authenticate. --- diff --git a/errors/codes/40142.md b/errors/codes/40142.md index 3b935198..f4b7c445 100644 --- a/errors/codes/40142.md +++ b/errors/codes/40142.md @@ -1,5 +1,6 @@ --- code: 40142 +identifier: token_expired title: Token expired summary: The client's connection or request was rejected because the authentication token had expired. Each token has an expiry time that is set when it is issued. --- diff --git a/errors/codes/40143.md b/errors/codes/40143.md index 5976bef6..d4effe73 100644 --- a/errors/codes/40143.md +++ b/errors/codes/40143.md @@ -1,5 +1,6 @@ --- code: 40143 +identifier: token_unrecognised title: Token not recognised summary: A connection or request was rejected because Ably could not find a record of the token presented. Ably stores a record of certain tokens only while they remain valid, so this often means the token had already expired. --- diff --git a/errors/codes/40144.md b/errors/codes/40144.md index 1cf55fca..82b6081c 100644 --- a/errors/codes/40144.md +++ b/errors/codes/40144.md @@ -1,5 +1,6 @@ --- code: 40144 +identifier: invalid_jwt title: Invalid JWT token summary: A JWT presented for authentication could not be parsed. The token was not well-formed, so it could not be read as a valid JSON Web Token. --- diff --git a/errors/codes/40145.md b/errors/codes/40145.md index 10a9564e..72eac999 100644 --- a/errors/codes/40145.md +++ b/errors/codes/40145.md @@ -1,5 +1,6 @@ --- code: 40145 +identifier: invalid_ably_token title: Invalid Ably token summary: An Ably token presented for authentication could not be parsed, so its contents could not be read. --- diff --git a/errors/codes/40150.md b/errors/codes/40150.md index 37f70780..40fe1806 100644 --- a/errors/codes/40150.md +++ b/errors/codes/40150.md @@ -1,5 +1,6 @@ --- code: 40150 +identifier: application_connection_limit_exceeded title: Application connection limit exceeded summary: A new connection was refused because the application had reached the maximum number of concurrent connections permitted for it. --- diff --git a/errors/codes/40160.md b/errors/codes/40160.md index 42635e57..f8e21a41 100644 --- a/errors/codes/40160.md +++ b/errors/codes/40160.md @@ -1,5 +1,6 @@ --- code: 40160 +identifier: capability_denied title: Client lacks the required capability summary: The API key or token used by the client isn't assigned the capability required for the operation — for example publishing, subscribing, or reading history on a channel, or listing channels and connections. --- diff --git a/errors/codes/40161.md b/errors/codes/40161.md index 7ff8a683..1b9fadfd 100644 --- a/errors/codes/40161.md +++ b/errors/codes/40161.md @@ -1,5 +1,6 @@ --- code: 40161 +identifier: identified_client_required title: Operation requires an identified client summary: The client had no established client ID, so an operation that requires one was refused. --- diff --git a/errors/codes/40162.md b/errors/codes/40162.md index 371f2575..d629602f 100644 --- a/errors/codes/40162.md +++ b/errors/codes/40162.md @@ -1,5 +1,6 @@ --- code: 40162 +identifier: operation_requires_basic_authentication title: Operation requires Basic authentication summary: The operation was refused because it can only be performed with Basic authentication using an API key, but the client authenticated with a token. Token revocation is one such operation, which must use the key that issued the tokens. --- diff --git a/errors/codes/40163.md b/errors/codes/40163.md index 11fa5c47..701924e4 100644 --- a/errors/codes/40163.md +++ b/errors/codes/40163.md @@ -1,5 +1,6 @@ --- code: 40163 +identifier: token_revocation_not_enabled_for_api_key title: Token revocation not enabled for API key summary: The operation was refused because the API key used does not have revocable tokens enabled. Revocation applies only to tokens issued by a key with that setting turned on. --- diff --git a/errors/codes/40164.md b/errors/codes/40164.md index 85e2f6db..f45ff083 100644 --- a/errors/codes/40164.md +++ b/errors/codes/40164.md @@ -1,5 +1,6 @@ --- code: 40164 +identifier: token_revocation_not_enabled_for_api_key_40164 title: Token revocation not enabled for API key summary: The operation was refused because the API key used does not have revocable tokens enabled. Revocation applies only to tokens issued by a key with that setting turned on. --- diff --git a/errors/codes/40165.md b/errors/codes/40165.md index e5e4c7ed..4dfd8f09 100644 --- a/errors/codes/40165.md +++ b/errors/codes/40165.md @@ -1,5 +1,6 @@ --- code: 40165 +identifier: channel_mode_not_requested_when_attaching title: Channel mode not requested when attaching summary: Publishing a message, object, or annotation — or entering presence — was rejected because the client did not request the matching channel mode when it attached. The credentials permit the operation; the mode that enables it was not requested. --- diff --git a/errors/codes/40166.md b/errors/codes/40166.md index 939335bc..22c72034 100644 --- a/errors/codes/40166.md +++ b/errors/codes/40166.md @@ -1,5 +1,6 @@ --- code: 40166 +identifier: unidentified_client_cannot_modify_own_messages title: Unidentified client cannot modify own messages summary: A message update or delete was rejected because the client is unidentified — it has no clientId — but holds only the "own" form of the relevant capability, message-update-own or message-delete-own, which can never apply to a client that has no messages of its own. --- diff --git a/errors/codes/40170.md b/errors/codes/40170.md index 3c969a9a..ed7ba80d 100644 --- a/errors/codes/40170.md +++ b/errors/codes/40170.md @@ -1,5 +1,6 @@ --- code: 40170 +identifier: error_from_client_token_callback title: Token callback failed summary: Authentication could not complete because the client's own token-request mechanism, its authCallback or authUrl, returned an error instead of a token or token request. The failure originates in the application's auth logic or endpoint, not within Ably. --- diff --git a/errors/codes/40171.md b/errors/codes/40171.md index b99f3f20..d3a59901 100644 --- a/errors/codes/40171.md +++ b/errors/codes/40171.md @@ -1,5 +1,6 @@ --- code: 40171 +identifier: token_renewal_not_configured title: Token renewal not configured summary: The auth token expired and could not be renewed because no renewal mechanism was configured. Without an authCallback, authUrl, or API key, the Ably SDK has no way to obtain a replacement token. --- diff --git a/errors/codes/40172.md b/errors/codes/40172.md index 618fbe84..2a70b731 100644 --- a/errors/codes/40172.md +++ b/errors/codes/40172.md @@ -1,5 +1,6 @@ --- code: 40172 +identifier: operation_requires_token_authentication title: Operation requires Token authentication summary: The operation was refused because it can only be performed with Token authentication, but the client authenticated using Basic authentication. Some operations are available only to clients using a token. --- diff --git a/errors/codes/40180.md b/errors/codes/40180.md index 71c41302..93dd1930 100644 --- a/errors/codes/40180.md +++ b/errors/codes/40180.md @@ -1,5 +1,6 @@ --- code: 40180 +identifier: apns_token_authentication_required title: APNs token authentication required summary: A location or live-activity notification was not sent because these require the app's APNs credentials to use token-based authentication, but the app is not configured that way. --- diff --git a/errors/codes/40181.md b/errors/codes/40181.md index 80ba9269..e1de9a63 100644 --- a/errors/codes/40181.md +++ b/errors/codes/40181.md @@ -1,5 +1,6 @@ --- code: 40181 +identifier: no_location_token_for_device title: No location token for device summary: A location push notification was not sent to a device because the device has no location token registered. Location notifications can only be delivered to devices that have supplied one. --- diff --git a/errors/codes/40300.md b/errors/codes/40300.md index 76856fce..520dd117 100644 --- a/errors/codes/40300.md +++ b/errors/codes/40300.md @@ -1,5 +1,6 @@ --- code: 40300 +identifier: forbidden title: Forbidden summary: The request was refused because it is not permitted. This covers a range of forbidden conditions, such as a disabled account or application, or a caller that lacks permission for the operation. --- diff --git a/errors/codes/40310.md b/errors/codes/40310.md index 20c93869..2c2ba680 100644 --- a/errors/codes/40310.md +++ b/errors/codes/40310.md @@ -1,5 +1,6 @@ --- code: 40310 +identifier: account_does_not_permit_tls_connections title: Account does not permit TLS connections summary: The connection was rejected because it used TLS, but the account is configured not to allow TLS connections. --- diff --git a/errors/codes/40311.md b/errors/codes/40311.md index 337e84fa..4beeabb5 100644 --- a/errors/codes/40311.md +++ b/errors/codes/40311.md @@ -1,5 +1,6 @@ --- code: 40311 +identifier: application_requires_a_tls_connection title: Application requires a TLS connection summary: The connection was rejected because it did not use TLS, but the application requires TLS connections. --- diff --git a/errors/codes/40320.md b/errors/codes/40320.md index ea55f020..77ceb8df 100644 --- a/errors/codes/40320.md +++ b/errors/codes/40320.md @@ -1,5 +1,6 @@ --- code: 40320 +identifier: authentication_required title: Authentication required summary: The request was rejected because it carried no authentication credentials. --- diff --git a/errors/codes/40330.md b/errors/codes/40330.md index 81d6f533..aa9de124 100644 --- a/errors/codes/40330.md +++ b/errors/codes/40330.md @@ -1,5 +1,6 @@ --- code: 40330 +identifier: account_not_permitted_in_cluster_or_region title: Account not permitted in this cluster or region summary: The request reached a cluster or region the account is not permitted to use; the accompanying error message gives the specific reason. --- diff --git a/errors/codes/40331.md b/errors/codes/40331.md index 6171f61a..dffba5c5 100644 --- a/errors/codes/40331.md +++ b/errors/codes/40331.md @@ -1,5 +1,6 @@ --- code: 40331 +identifier: account_not_permitted_in_cluster title: Account not permitted in this cluster summary: The request reached an Ably cluster the account is not permitted to use. --- diff --git a/errors/codes/40332.md b/errors/codes/40332.md index 5e83727b..61e06bc8 100644 --- a/errors/codes/40332.md +++ b/errors/codes/40332.md @@ -1,5 +1,6 @@ --- code: 40332 +identifier: account_not_permitted_in_region title: Account not permitted in this region summary: The request reached a region the account is not permitted to use. --- diff --git a/errors/codes/40400.md b/errors/codes/40400.md index 5afeff27..de21e4b8 100644 --- a/errors/codes/40400.md +++ b/errors/codes/40400.md @@ -1,5 +1,6 @@ --- code: 40400 +identifier: not_found title: Resource not found summary: The requested resource does not exist, often because it has been deleted or the path or identifier used to reference it is incorrect. --- diff --git a/errors/codes/40500.md b/errors/codes/40500.md index b4735f74..ad9029b4 100644 --- a/errors/codes/40500.md +++ b/errors/codes/40500.md @@ -1,5 +1,6 @@ --- code: 40500 +identifier: method_not_allowed title: HTTP method not allowed summary: The request used an HTTP method that the endpoint does not support. --- diff --git a/errors/codes/40900.md b/errors/codes/40900.md index ddfecfc0..4d27c66d 100644 --- a/errors/codes/40900.md +++ b/errors/codes/40900.md @@ -1,5 +1,6 @@ --- code: 40900 +identifier: conflict title: Conflict summary: The request conflicted with the current state of the target, so it could not be completed. --- diff --git a/errors/codes/41001.md b/errors/codes/41001.md index 3834c192..9671c143 100644 --- a/errors/codes/41001.md +++ b/errors/codes/41001.md @@ -1,5 +1,6 @@ --- code: 41001 +identifier: push_device_registration_expired title: Push device registration expired summary: The push notification could not be delivered because the target device's registration is no longer valid. Registrations expire over time, and the device must register again before it can receive notifications. --- diff --git a/errors/codes/42200.md b/errors/codes/42200.md index 95d29698..4753ebfd 100644 --- a/errors/codes/42200.md +++ b/errors/codes/42200.md @@ -1,5 +1,6 @@ --- code: 42200 +identifier: unprocessable_content title: Message content could not be processed summary: A message was rejected because a content check — for example validation or moderation — did not accept it, rather than for a syntax or size problem. --- diff --git a/errors/codes/42210.md b/errors/codes/42210.md index b293b4aa..d5213304 100644 --- a/errors/codes/42210.md +++ b/errors/codes/42210.md @@ -1,5 +1,6 @@ --- code: 42210 +identifier: content_rejected title: Message content rejected summary: A message was rejected by a content check — such as validation, moderation, or an integration — but the error does not identify which one. --- diff --git a/errors/codes/42211.md b/errors/codes/42211.md index e23592de..33b3d76c 100644 --- a/errors/codes/42211.md +++ b/errors/codes/42211.md @@ -1,5 +1,6 @@ --- code: 42211 +identifier: content_rejected_by_before_publish_integration title: Message rejected by integration summary: A message was rejected by an integration configured to check messages before they are accepted. --- diff --git a/errors/codes/42212.md b/errors/codes/42212.md index c1dc3471..7f05456f 100644 --- a/errors/codes/42212.md +++ b/errors/codes/42212.md @@ -1,5 +1,6 @@ --- code: 42212 +identifier: content_rejected_by_validation title: Message rejected by content validation summary: A message was rejected because it failed a content-validation check. --- diff --git a/errors/codes/42213.md b/errors/codes/42213.md index 230440d0..9109c8f3 100644 --- a/errors/codes/42213.md +++ b/errors/codes/42213.md @@ -1,5 +1,6 @@ --- code: 42213 +identifier: content_rejected_by_moderation title: Message rejected by moderation summary: A message was rejected by a content-moderation check configured on the channel. The moderation check inspected the content and flagged it as not permitted. --- diff --git a/errors/codes/42910.md b/errors/codes/42910.md index af15868c..18cd6f44 100644 --- a/errors/codes/42910.md +++ b/errors/codes/42910.md @@ -1,5 +1,6 @@ --- code: 42910 +identifier: rate_limit_exceeded_generic title: System rate limit exceeded summary: An operation was rejected because a system rate limit was exceeded. The limit protects Ably against overload; the operation can be retried. --- diff --git a/errors/codes/42911.md b/errors/codes/42911.md index 86d62e5d..f759db62 100644 --- a/errors/codes/42911.md +++ b/errors/codes/42911.md @@ -1,5 +1,6 @@ --- code: 42911 +identifier: rate_limit_exceeded_per_connection_inbound title: Per-connection publish rate exceeded summary: A message was rejected because the connection published messages faster than the per-connection publish rate limit allows. Publishing far above the limit can escalate to the connection being closed. --- diff --git a/errors/codes/42912.md b/errors/codes/42912.md index 175548fb..5d2d8d2e 100644 --- a/errors/codes/42912.md +++ b/errors/codes/42912.md @@ -1,5 +1,6 @@ --- code: 42912 +identifier: rate_limit_channel_iteration_in_progress title: Channel enumeration already in progress summary: A request to enumerate the channels active in an app was rejected because another enumeration was already running. Only one channel enumeration can run at a time for an app. --- diff --git a/errors/codes/42913.md b/errors/codes/42913.md index aefaff18..7af82725 100644 --- a/errors/codes/42913.md +++ b/errors/codes/42913.md @@ -1,5 +1,6 @@ --- code: 42913 +identifier: rate_limit_exceeded_per_channel_inbound title: Per-channel publish rate exceeded summary: A message was rejected because the rate of messages published to the channel exceeded its configured limit. The limit applies to all publishers to the channel combined, so it can be reached even when no single publisher is publishing quickly. --- diff --git a/errors/codes/42914.md b/errors/codes/42914.md index de3be726..f4107ba6 100644 --- a/errors/codes/42914.md +++ b/errors/codes/42914.md @@ -1,5 +1,6 @@ --- code: 42914 +identifier: rate_limit_exceeded_per_channel_bandwidth title: Per-channel publish bandwidth exceeded summary: A message was rejected because the rate of data published to the channel exceeded its configured bandwidth limit. The limit applies to all publishers to the channel combined, so large messages can reach it even at a low message rate. --- diff --git a/errors/codes/42915.md b/errors/codes/42915.md index f0a22fac..f2a27850 100644 --- a/errors/codes/42915.md +++ b/errors/codes/42915.md @@ -1,5 +1,6 @@ --- code: 42915 +identifier: rate_limit_exceeded_per_connection_outbound title: Per-connection outbound message rate exceeded summary: Messages were not delivered to a connection because the rate of messages being sent to it exceeded its per-connection outbound limit. The client is notified of the gap by a channel continuity loss, which it receives as error 80020. --- diff --git a/errors/codes/42916.md b/errors/codes/42916.md index 3a9f38cb..17e952e4 100644 --- a/errors/codes/42916.md +++ b/errors/codes/42916.md @@ -1,5 +1,6 @@ --- code: 42916 +identifier: rate_limit_exceeded_per_connection_backlog title: Per-connection outbound resume rate exceeded summary: Backlog messages were not delivered to a connection because the rate of delivering them exceeded its per-connection outbound limit. The client is notified of the gap by a channel continuity loss, which it receives as error 80020. --- diff --git a/errors/codes/42917.md b/errors/codes/42917.md index 2f79e82f..c9ef293e 100644 --- a/errors/codes/42917.md +++ b/errors/codes/42917.md @@ -1,5 +1,6 @@ --- code: 42917 +identifier: rate_limit_exceeded_account_messages title: Account-wide message publish rate exceeded summary: A message was rejected because the rate of messages published across the account exceeded the configured account-wide limit. A proportion of messages are rejected to bring the account back within its limit. --- diff --git a/errors/codes/42918.md b/errors/codes/42918.md index 14c26b8f..e4da512d 100644 --- a/errors/codes/42918.md +++ b/errors/codes/42918.md @@ -1,5 +1,6 @@ --- code: 42918 +identifier: rate_limit_exceeded_account_api_requests title: Account-wide API request rate exceeded summary: A REST API request was rejected because the request rate across the account exceeded the configured account-wide limit. A proportion of requests are rejected to bring the account back within its limit. --- diff --git a/errors/codes/42920.md b/errors/codes/42920.md index 55d96200..4cd4d2b8 100644 --- a/errors/codes/42920.md +++ b/errors/codes/42920.md @@ -1,5 +1,6 @@ --- code: 42920 +identifier: rate_limit_exceeded_connection_fatal title: Connection terminated by a fatal rate limit summary: The connection was closed because a rate limit was exceeded severely enough to be treated as fatal, rather than rejecting individual operations. This is the general form of a fatal rate-limit close; common cases have their own codes. --- diff --git a/errors/codes/42921.md b/errors/codes/42921.md index 653b43b4..678861fd 100644 --- a/errors/codes/42921.md +++ b/errors/codes/42921.md @@ -1,5 +1,6 @@ --- code: 42921 +identifier: rate_limit_exceeded_per_connection_inbound_fatal title: Connection terminated for far exceeding the per-connection publish rate summary: The connection was closed because it published far above the per-connection publish rate limit — around 20 times the permitted rate. Breaches below that reject individual messages without closing the connection. --- diff --git a/errors/codes/42922.md b/errors/codes/42922.md index 521106dc..0cdac6fd 100644 --- a/errors/codes/42922.md +++ b/errors/codes/42922.md @@ -1,5 +1,6 @@ --- code: 42922 +identifier: rate_limit_exceeded_too_many_requests title: Too many requests summary: A request was blocked because too many requests were received in a short period, triggering Ably's flood protection. This protects the service against excessive or abusive traffic, separately from your account's own rate limits. --- diff --git a/errors/codes/42923.md b/errors/codes/42923.md index 22daf5fd..649b5bea 100644 --- a/errors/codes/42923.md +++ b/errors/codes/42923.md @@ -1,5 +1,6 @@ --- code: 42923 +identifier: integration_target_rate_limit_response title: Integration target responded with rate limit error summary: An integration target such as an HTTP endpoint, serverless function, or message queue returned a rate-limit response when Ably invoked it. The limit is the target's own, not one of Ably's. --- diff --git a/errors/codes/42924.md b/errors/codes/42924.md index a6357699..9563be06 100644 --- a/errors/codes/42924.md +++ b/errors/codes/42924.md @@ -1,5 +1,6 @@ --- code: 42924 +identifier: rate_limit_exceeded_protocol_message_rate_fatal title: Per-connection protocol message rate exceeded summary: A connection was terminated because the rate of inbound protocol messages from the connection exceeded its configured per-connection limit. Protocol messages include message publishes, presence updates, attach/detach requests, and other client-initiated actions on the connection. --- diff --git a/errors/codes/42925.md b/errors/codes/42925.md index 0ba207c9..2b5a443e 100644 --- a/errors/codes/42925.md +++ b/errors/codes/42925.md @@ -1,5 +1,6 @@ --- code: 42925 +identifier: rate_limit_exceeded_account_integrations title: Account-wide integration invocation rate exceeded summary: An integration invocation was dropped because the invocation rate across the account exceeded the configured account-wide limit. A proportion of invocations are dropped to bring the account back within its limit. --- diff --git a/errors/codes/42926.md b/errors/codes/42926.md index 30d2fdae..2a6d1b8b 100644 --- a/errors/codes/42926.md +++ b/errors/codes/42926.md @@ -1,5 +1,6 @@ --- code: 42926 +identifier: rate_limit_exceeded_account_push_notifications title: Account-wide push notification rate exceeded summary: A push notification was dropped because the publish rate of push notifications across the account exceeded the configured account-wide limit. A proportion of push notifications are dropped to bring the account back within its limit. --- diff --git a/errors/codes/50000.md b/errors/codes/50000.md index b3bc37db..30690a6e 100644 --- a/errors/codes/50000.md +++ b/errors/codes/50000.md @@ -1,5 +1,6 @@ --- code: 50000 +identifier: internal_error title: Internal server error summary: The request could not be completed because of an unexpected error inside the Ably platform. --- diff --git a/errors/codes/50001.md b/errors/codes/50001.md index 5f80e142..97a21ee8 100644 --- a/errors/codes/50001.md +++ b/errors/codes/50001.md @@ -1,5 +1,6 @@ --- code: 50001 +identifier: internal_channel_error title: Internal channel error summary: An operation on a channel failed because of an unexpected error inside the Ably platform. --- diff --git a/errors/codes/50002.md b/errors/codes/50002.md index 2710c1c4..dce9c2c8 100644 --- a/errors/codes/50002.md +++ b/errors/codes/50002.md @@ -1,5 +1,6 @@ --- code: 50002 +identifier: internal_connection_error title: Internal connection error summary: An operation on a connection failed because of an unexpected error inside the Ably platform. --- diff --git a/errors/codes/50003.md b/errors/codes/50003.md index d1ade2b8..5dc15fdd 100644 --- a/errors/codes/50003.md +++ b/errors/codes/50003.md @@ -1,5 +1,6 @@ --- code: 50003 +identifier: timeout_error title: Request timed out server-side summary: The Ably platform did not finish handling the request within the time allowed, so the request was abandoned. This usually reflects a transient server-side delay rather than a problem with the request. --- diff --git a/errors/codes/50004.md b/errors/codes/50004.md index 35dc920b..03ab74ce 100644 --- a/errors/codes/50004.md +++ b/errors/codes/50004.md @@ -1,5 +1,6 @@ --- code: 50004 +identifier: server_temporarily_busy title: Server temporarily busy summary: The request was rejected because the server handling it could not accept it at that moment. The condition is transient. --- diff --git a/errors/codes/50005.md b/errors/codes/50005.md index 6b101ce6..69380e7b 100644 --- a/errors/codes/50005.md +++ b/errors/codes/50005.md @@ -1,5 +1,6 @@ --- code: 50005 +identifier: service_temporarily_unavailable title: Service temporarily unavailable summary: The request could not be served because the service was temporarily in a locked-down state, during which requests are not accepted. The condition is temporary and expected to clear. --- diff --git a/errors/codes/50006.md b/errors/codes/50006.md index bee7085c..11ab5f53 100644 --- a/errors/codes/50006.md +++ b/errors/codes/50006.md @@ -1,5 +1,6 @@ --- code: 50006 +identifier: connection_superseded_by_a_newer_connection title: Connection superseded by a newer connection summary: The connection's state was taken over by a more recent connection, typically a reconnection or resume. This only arises on a deprecated Ably protocol version; upgrade to a recent Ably SDK release to avoid it. --- diff --git a/errors/codes/50010.md b/errors/codes/50010.md index 38910d27..bbb87f4b 100644 --- a/errors/codes/50010.md +++ b/errors/codes/50010.md @@ -1,5 +1,6 @@ --- code: 50010 +identifier: edge_proxy_internal_error title: Edge proxy internal error summary: Ably's edge proxy service encountered an unknown internal error while handling the request and could not complete it. --- diff --git a/errors/codes/50210.md b/errors/codes/50210.md index cae48b64..b58c72a6 100644 --- a/errors/codes/50210.md +++ b/errors/codes/50210.md @@ -1,5 +1,6 @@ --- code: 50210 +identifier: invalid_platform_response title: Invalid response from the platform summary: Ably's edge proxy service received an invalid response from the Ably platform behind it and could not complete the request. The condition is usually transient. --- diff --git a/errors/codes/50310.md b/errors/codes/50310.md index cf4a3cac..e3700fcc 100644 --- a/errors/codes/50310.md +++ b/errors/codes/50310.md @@ -1,5 +1,6 @@ --- code: 50310 +identifier: platform_temporarily_unavailable title: Platform temporarily unavailable summary: Ably's edge proxy service received a service-unavailable response from the Ably platform behind it and could not complete the request. The condition is usually transient. --- diff --git a/errors/codes/50320.md b/errors/codes/50320.md index 2ef1f8ae..3f59f899 100644 --- a/errors/codes/50320.md +++ b/errors/codes/50320.md @@ -1,5 +1,6 @@ --- code: 50320 +identifier: traffic_temporarily_redirected title: Traffic temporarily redirected summary: Ably's Active Traffic Management temporarily redirected traffic away from its normal path, and the request was affected while that redirection was in place. The condition is temporary. --- diff --git a/errors/codes/50330.md b/errors/codes/50330.md index f90af9cb..d04abf86 100644 --- a/errors/codes/50330.md +++ b/errors/codes/50330.md @@ -1,5 +1,6 @@ --- code: 50330 +identifier: request_reached_the_wrong_cluster title: Request reached the wrong cluster summary: The request arrived at a cluster that was not the one meant to handle it and should be retried. This can happen briefly during DNS changes while traffic is being migrated between clusters. --- diff --git a/errors/codes/50410.md b/errors/codes/50410.md index 1d06b8c3..b37499e4 100644 --- a/errors/codes/50410.md +++ b/errors/codes/50410.md @@ -1,5 +1,6 @@ --- code: 50410 +identifier: edge_proxy_timed_out_waiting_for_platform title: Edge proxy timed out waiting for platform summary: Ably's edge proxy service did not receive a response from the Ably platform behind it within the time allowed, so the request was abandoned. The condition is usually transient. --- diff --git a/errors/codes/70000.md b/errors/codes/70000.md index 8b5b74b1..6c2b4756 100644 --- a/errors/codes/70000.md +++ b/errors/codes/70000.md @@ -1,5 +1,6 @@ --- code: 70000 +identifier: integration_operation_failed title: Integration operation failed summary: An integration could not complete an operation against its configured target. --- diff --git a/errors/codes/70001.md b/errors/codes/70001.md index a4260b9c..dee2640a 100644 --- a/errors/codes/70001.md +++ b/errors/codes/70001.md @@ -1,5 +1,6 @@ --- code: 70001 +identifier: integration_invocation_failed title: Integration invocation failed summary: An integration could not invoke its configured target: Ably either could not reach the target, or reached it but could not deliver the data. The specific failure depends on the integration type and is named in the error message. --- diff --git a/errors/codes/70002.md b/errors/codes/70002.md index 946bd222..d6c3db0c 100644 --- a/errors/codes/70002.md +++ b/errors/codes/70002.md @@ -1,5 +1,6 @@ --- code: 70002 +identifier: integration_target_returned_an_error_status title: Integration target returned an error status summary: An integration invoked an HTTP target, such as a webhook, which replied with an HTTP status outside the successful 2xx range. Ably treats any non-2xx response as a failed delivery. --- diff --git a/errors/codes/70003.md b/errors/codes/70003.md index 58768c4f..de369e8e 100644 --- a/errors/codes/70003.md +++ b/errors/codes/70003.md @@ -1,5 +1,6 @@ --- code: 70003 +identifier: too_many_concurrent_integration_requests title: Too many concurrent integration requests summary: An integration was not invoked because the number of requests already in flight for it had reached the permitted maximum. This usually happens when a target responds more slowly than the rate at which matching messages arrive. --- diff --git a/errors/codes/70004.md b/errors/codes/70004.md index ae0e7d00..0166a98b 100644 --- a/errors/codes/70004.md +++ b/errors/codes/70004.md @@ -1,5 +1,6 @@ --- code: 70004 +identifier: integration_refused_to_process_a_message title: Integration refused to process a message summary: An integration reached its configured target, but the target did not accept the message because its contents were invalid or unsupported for that target type. --- diff --git a/errors/codes/70005.md b/errors/codes/70005.md index dea53f3f..72076f29 100644 --- a/errors/codes/70005.md +++ b/errors/codes/70005.md @@ -1,5 +1,6 @@ --- code: 70005 +identifier: amqp_message_delivery_timed_out title: AMQP message delivery timed out summary: A message could not be delivered to an Ably message queue integration because the delivery did not complete within the allowed time. This usually points to the queue being unavailable or slow to accept the message. --- diff --git a/errors/codes/70006.md b/errors/codes/70006.md index da37f193..11d7f0b1 100644 --- a/errors/codes/70006.md +++ b/errors/codes/70006.md @@ -1,5 +1,6 @@ --- code: 70006 +identifier: amqp_message_queue_busy title: AMQP message queue busy summary: A message could not be delivered to an Ably message queue integration because the message queue was too busy to accept it. This is usually transient and eases as load falls. --- diff --git a/errors/codes/71000.md b/errors/codes/71000.md index 0258c217..b22a5d5b 100644 --- a/errors/codes/71000.md +++ b/errors/codes/71000.md @@ -1,5 +1,6 @@ --- code: 71000 +identifier: exchange_error title: Exchange error summary: A request involving Ably Exchange failed for a reason that could not be attributed to a more specific cause. Exchange lets a publisher expose channels as a product that subscribers can receive. --- diff --git a/errors/codes/71001.md b/errors/codes/71001.md index 852398a9..d9788d34 100644 --- a/errors/codes/71001.md +++ b/errors/codes/71001.md @@ -1,5 +1,6 @@ --- code: 71001 +identifier: forced_re_attachment_after_permissions_change title: Forced re-attachment after permissions change summary: An Exchange channel was detached and re-attached because the permissions governing access to it changed, so the existing attachment no longer reflected what the client was entitled to receive. --- diff --git a/errors/codes/71100.md b/errors/codes/71100.md index c87d29c7..48120834 100644 --- a/errors/codes/71100.md +++ b/errors/codes/71100.md @@ -1,5 +1,6 @@ --- code: 71100 +identifier: exchange_publisher_error title: Exchange publisher error summary: A request involving an Exchange publisher failed for a reason that could not be attributed to a more specific cause. A publisher is the party that exposes channels as an Exchange product. --- diff --git a/errors/codes/71101.md b/errors/codes/71101.md index 90d9fc2a..1698119c 100644 --- a/errors/codes/71101.md +++ b/errors/codes/71101.md @@ -1,5 +1,6 @@ --- code: 71101 +identifier: publisher_not_found title: Publisher not found summary: The Exchange request referred to a publisher that does not exist. This usually means the publisher identifier was incorrect or the publisher is no longer available. --- diff --git a/errors/codes/71102.md b/errors/codes/71102.md index 546cfb59..7480f7cf 100644 --- a/errors/codes/71102.md +++ b/errors/codes/71102.md @@ -1,5 +1,6 @@ --- code: 71102 +identifier: publisher_not_enabled_for_exchange title: Publisher not enabled for Exchange summary: The request referred to a party that exists but has not been enabled as an Exchange publisher, so it cannot expose channels as a product for subscribers to receive. --- diff --git a/errors/codes/71200.md b/errors/codes/71200.md index 58e5f4f0..6c64bc1d 100644 --- a/errors/codes/71200.md +++ b/errors/codes/71200.md @@ -1,5 +1,6 @@ --- code: 71200 +identifier: exchange_product_error title: Exchange product error summary: A request involving an Exchange product failed for a reason that could not be attributed to a more specific cause. A product is the set of channels a publisher exposes for subscribers to receive. --- diff --git a/errors/codes/71201.md b/errors/codes/71201.md index 65e42321..c0cd9f45 100644 --- a/errors/codes/71201.md +++ b/errors/codes/71201.md @@ -1,5 +1,6 @@ --- code: 71201 +identifier: product_not_found title: Product not found summary: The Exchange request referred to a product that does not exist. This usually means the product identifier was incorrect or the product is no longer available from the publisher. --- diff --git a/errors/codes/71202.md b/errors/codes/71202.md index b82e0844..3f674cf0 100644 --- a/errors/codes/71202.md +++ b/errors/codes/71202.md @@ -1,5 +1,6 @@ --- code: 71202 +identifier: product_disabled title: Product disabled summary: The Exchange product exists but has been disabled by the publisher, so its channels are not currently available to subscribers. --- diff --git a/errors/codes/71203.md b/errors/codes/71203.md index b18c6b35..1a3ae296 100644 --- a/errors/codes/71203.md +++ b/errors/codes/71203.md @@ -1,5 +1,6 @@ --- code: 71203 +identifier: channel_not_part_of_product title: Channel not part of product summary: The requested channel is not one of the channels that the Exchange product exposes, so it cannot be received through that product. --- diff --git a/errors/codes/71204.md b/errors/codes/71204.md index 9d3b98ac..56565fdd 100644 --- a/errors/codes/71204.md +++ b/errors/codes/71204.md @@ -1,5 +1,6 @@ --- code: 71204 +identifier: forced_re_attachment_after_product_remapping title: Forced re-attachment after product remapping summary: An Exchange channel was detached and re-attached because the product was remapped to a different channel namespace, so the existing attachment no longer pointed at the channel the product now exposes. --- diff --git a/errors/codes/71300.md b/errors/codes/71300.md index a4e24df6..9e8a58a4 100644 --- a/errors/codes/71300.md +++ b/errors/codes/71300.md @@ -1,5 +1,6 @@ --- code: 71300 +identifier: exchange_subscription_error title: Exchange subscription error summary: A request involving an Exchange subscription failed for a reason that could not be attributed to a more specific cause. A subscription is what grants a subscriber access to a publisher's product. --- diff --git a/errors/codes/71301.md b/errors/codes/71301.md index cfb36272..e22522ab 100644 --- a/errors/codes/71301.md +++ b/errors/codes/71301.md @@ -1,5 +1,6 @@ --- code: 71301 +identifier: subscription_disabled title: Subscription disabled summary: The subscription to the Exchange product exists but has been disabled, so its channels are not currently available to the subscriber. --- diff --git a/errors/codes/71302.md b/errors/codes/71302.md index a67395fb..31599d66 100644 --- a/errors/codes/71302.md +++ b/errors/codes/71302.md @@ -1,5 +1,6 @@ --- code: 71302 +identifier: no_subscription_to_product title: No subscription to product summary: The requester attempted to receive an Exchange product they are not subscribed to. Access to a product's channels requires an active subscription to that product. --- diff --git a/errors/codes/71303.md b/errors/codes/71303.md index 4ad1f1ca..fd347b78 100644 --- a/errors/codes/71303.md +++ b/errors/codes/71303.md @@ -1,5 +1,6 @@ --- code: 71303 +identifier: channel_outside_subscription_filter title: Channel outside subscription filter summary: The requested channel does not match the channel filter defined for the subscription to the Exchange product, so it falls outside the set of channels that subscription grants access to. --- diff --git a/errors/codes/72000.md b/errors/codes/72000.md index 502bdcf9..065884cf 100644 --- a/errors/codes/72000.md +++ b/errors/codes/72000.md @@ -1,5 +1,6 @@ --- code: 72000 +identifier: livesync_operation_failed title: LiveSync connector operation failed summary: A LiveSync database connector hit an unexpected error while running, so the operation it was attempting did not complete. --- diff --git a/errors/codes/72001.md b/errors/codes/72001.md index c8a980a4..e25704d4 100644 --- a/errors/codes/72001.md +++ b/errors/codes/72001.md @@ -1,5 +1,6 @@ --- code: 72001 +identifier: livesync_publish_failed title: LiveSync connector could not publish summary: A LiveSync database connector read a change from the database but could not publish the resulting message to Ably. The change may not have reached the channel it was destined for. --- diff --git a/errors/codes/72002.md b/errors/codes/72002.md index 2017e61b..4ba8d611 100644 --- a/errors/codes/72002.md +++ b/errors/codes/72002.md @@ -1,5 +1,6 @@ --- code: 72002 +identifier: livesync_table_unhealthy title: LiveSync database connector is unhealthy summary: A LiveSync database connector could not read from the table or collection it is configured to use, because that table or collection is missing, renamed, or no longer accessible to the connector. --- diff --git a/errors/codes/72003.md b/errors/codes/72003.md index 91e78fa7..0457c157 100644 --- a/errors/codes/72003.md +++ b/errors/codes/72003.md @@ -1,5 +1,6 @@ --- code: 72003 +identifier: livesync_cannot_connect_db title: LiveSync connector cannot reach the database summary: A LiveSync database connector could not establish a connection to the database it reads from. This often points to the database being unreachable, or to incorrect connection details or credentials in the connector configuration. --- diff --git a/errors/codes/72004.md b/errors/codes/72004.md index 6137a234..8ab933ba 100644 --- a/errors/codes/72004.md +++ b/errors/codes/72004.md @@ -1,5 +1,6 @@ --- code: 72004 +identifier: livesync_no_channel_key title: LiveSync connector could not identify target channel summary: A LiveSync database connector read a change but could not determine which channel to publish it to, because the change had no _ablyChannel field. Only changes that specify a channel can be published. --- diff --git a/errors/codes/72005.md b/errors/codes/72005.md index 16685972..c700fac3 100644 --- a/errors/codes/72005.md +++ b/errors/codes/72005.md @@ -1,5 +1,6 @@ --- code: 72005 +identifier: livesync_invalid_pipeline title: LiveSync MongoDB pipeline is invalid summary: A LiveSync MongoDB connector could not start because the aggregation pipeline in its configuration was not accepted by MongoDB, for example because it references a stage or field that is not allowed. --- diff --git a/errors/codes/72006.md b/errors/codes/72006.md index a68b652b..fad045f8 100644 --- a/errors/codes/72006.md +++ b/errors/codes/72006.md @@ -1,5 +1,6 @@ --- code: 72006 +identifier: livesync_cannot_resume title: LiveSync connector failed to resume summary: A LiveSync MongoDB connector could not resume its change stream from where it left off. It stores a resume token to continue after a restart, and this arises when that stored position cannot be read back. --- diff --git a/errors/codes/72007.md b/errors/codes/72007.md index d56f0e24..047e5695 100644 --- a/errors/codes/72007.md +++ b/errors/codes/72007.md @@ -1,5 +1,6 @@ --- code: 72007 +identifier: livesync_cannot_store_resume_token title: LiveSync connector failed to track its position summary: A LiveSync MongoDB connector could not save its change-stream resume token to the database. The token records how far the connector has read, so that it can continue from the same point after a restart. --- diff --git a/errors/codes/72008.md b/errors/codes/72008.md index a0e7586b..18a0d24b 100644 --- a/errors/codes/72008.md +++ b/errors/codes/72008.md @@ -1,5 +1,6 @@ --- code: 72008 +identifier: livesync_change_stream_history_lost title: LiveSync connector failed to read history summary: A LiveSync MongoDB connector's change stream could no longer continue from its last recorded position, because the history it needed was no longer available in the database. Changes made in the intervening period may have been missed. --- diff --git a/errors/codes/80000.md b/errors/codes/80000.md index b503ac92..51aac8ca 100644 --- a/errors/codes/80000.md +++ b/errors/codes/80000.md @@ -1,5 +1,6 @@ --- code: 80000 +identifier: connection_failed title: Connection failed summary: The connection moved to the failed state, a terminal condition from which the SDK does not automatically reconnect. It differs from a temporary disconnection, where the SDK keeps retrying to restore the connection on its own. --- diff --git a/errors/codes/80001.md b/errors/codes/80001.md index 9ab22ae1..7d16e52c 100644 --- a/errors/codes/80001.md +++ b/errors/codes/80001.md @@ -1,5 +1,6 @@ --- code: 80001 +identifier: connection_failed_no_transport title: Connection failed as no usable transport available summary: No usable transport could be established to carry the connection, for example when the network blocks both WebSocket and the HTTP-based fallbacks. --- diff --git a/errors/codes/80002.md b/errors/codes/80002.md index 247cdd89..795f1034 100644 --- a/errors/codes/80002.md +++ b/errors/codes/80002.md @@ -1,5 +1,6 @@ --- code: 80002 +identifier: connection_suspended title: Connection suspended summary: The connection moved to the suspended state after being disconnected for an extended period. A connection becomes suspended once it has been disconnected for around two minutes, beyond which its state can no longer be recovered. --- diff --git a/errors/codes/80003.md b/errors/codes/80003.md index c3965ddf..edaa7c8c 100644 --- a/errors/codes/80003.md +++ b/errors/codes/80003.md @@ -1,5 +1,6 @@ --- code: 80003 +identifier: connection_disconnected title: Connection disconnected summary: The connection to Ably was dropped. This is a normal, usually brief interruption, such as a network change or Ably cycling the connection, rather than a deliberate close, and the connection is expected to be re-established. --- diff --git a/errors/codes/80004.md b/errors/codes/80004.md index e2f8f357..f98dbf1c 100644 --- a/errors/codes/80004.md +++ b/errors/codes/80004.md @@ -1,5 +1,6 @@ --- code: 80004 +identifier: connection_already_established title: Connection already established summary: A request to open a connection was made while a connection was already active. --- diff --git a/errors/codes/80005.md b/errors/codes/80005.md index 2816e160..aa55b269 100644 --- a/errors/codes/80005.md +++ b/errors/codes/80005.md @@ -1,5 +1,6 @@ --- code: 80005 +identifier: connection_no_longer_available title: Connection no longer available summary: A request referenced an existing connection, but the server no longer held its state — typically because the connection had been idle and was released after a period without activity. The client is signalled to reconnect and continue on a fresh connection. --- diff --git a/errors/codes/80006.md b/errors/codes/80006.md index 25e0e1ad..1b7409ed 100644 --- a/errors/codes/80006.md +++ b/errors/codes/80006.md @@ -1,5 +1,6 @@ --- code: 80006 +identifier: connection_messages_expired title: Connection continuity not guaranteed as messages expired summary: A connection was resumed after the messages needed to bridge the gap had expired. The connection continues, but any messages published during the disconnection may not be redelivered, so continuity across it is not guaranteed. --- diff --git a/errors/codes/80007.md b/errors/codes/80007.md index 527c735c..1c0fe6d7 100644 --- a/errors/codes/80007.md +++ b/errors/codes/80007.md @@ -1,5 +1,6 @@ --- code: 80007 +identifier: connection_message_limit_exceeded title: Connection continuity not guaranteed as message limit exceeded summary: A connection was resumed, but more messages accumulated during the disconnection than can be held for recovery. The connection continues, but some of those messages may not be redelivered, so continuity across the gap is not guaranteed. --- diff --git a/errors/codes/80008.md b/errors/codes/80008.md index ead4ecf0..662e77ff 100644 --- a/errors/codes/80008.md +++ b/errors/codes/80008.md @@ -1,5 +1,6 @@ --- code: 80008 +identifier: connection_recovery_failed_on_an_outdated_sdk title: Connection recovery failed on an outdated SDK summary: An older Ably SDK could not recover a dropped connection. This code is produced only by SDK versions that use a retired version of the Ably protocol; current SDKs connect differently and do not report it. --- diff --git a/errors/codes/80009.md b/errors/codes/80009.md index ed72b180..f06fa4b9 100644 --- a/errors/codes/80009.md +++ b/errors/codes/80009.md @@ -1,5 +1,6 @@ --- code: 80009 +identifier: connection_not_established title: Connection not established summary: An operation that requires an open connection was attempted while the client was not connected. --- diff --git a/errors/codes/80010.md b/errors/codes/80010.md index f4cf8dad..7be7038f 100644 --- a/errors/codes/80010.md +++ b/errors/codes/80010.md @@ -1,5 +1,6 @@ --- code: 80010 +identifier: invalid_operation_on_the_connection title: Invalid operation on the connection summary: An operation was attempted on a connection that was not in a valid state to carry it, such as one that had already been closed or replaced. --- diff --git a/errors/codes/80011.md b/errors/codes/80011.md index c800daa4..22b59c6c 100644 --- a/errors/codes/80011.md +++ b/errors/codes/80011.md @@ -1,5 +1,6 @@ --- code: 80011 +identifier: connection_incompatible_auth_params title: Connection state recovery failed due to incompatible auth summary: A dropped connection could not be recovered because the authentication details presented on the new connection did not match those of the original, so the previous connection could not be continued. --- diff --git a/errors/codes/80012.md b/errors/codes/80012.md index e2abc461..3adb045f 100644 --- a/errors/codes/80012.md +++ b/errors/codes/80012.md @@ -1,5 +1,6 @@ --- code: 80012 +identifier: connection_invalid_serial title: Connection continuity not guaranteed as serial was invalid summary: A connection was resumed without a valid connection serial to identify the point it had reached, so there was no reliable position to continue from. The connection continues, but continuity across the gap is not guaranteed. --- diff --git a/errors/codes/80013.md b/errors/codes/80013.md index b28184cb..c985d295 100644 --- a/errors/codes/80013.md +++ b/errors/codes/80013.md @@ -1,5 +1,6 @@ --- code: 80013 +identifier: protocol_error title: Protocol error summary: A message exchanged over the connection did not conform to the Ably protocol. The connection received something it could not interpret as a valid protocol message. --- diff --git a/errors/codes/80014.md b/errors/codes/80014.md index b6e49fb1..4e35bfb8 100644 --- a/errors/codes/80014.md +++ b/errors/codes/80014.md @@ -1,5 +1,6 @@ --- code: 80014 +identifier: connection_timed_out title: Connection timed out summary: The connection was not established, or a response was not received, within the time allowed. This often points to a slow or unreliable network path between the client and Ably. --- diff --git a/errors/codes/80015.md b/errors/codes/80015.md index 632a4029..afd9ecbe 100644 --- a/errors/codes/80015.md +++ b/errors/codes/80015.md @@ -1,5 +1,6 @@ --- code: 80015 +identifier: incompatible_connection_parameters title: Incompatible connection parameters summary: The connection could not be established because the parameters supplied when opening it were not compatible with one another or with what Ably supports. --- diff --git a/errors/codes/80016.md b/errors/codes/80016.md index cd6d53d6..62270e30 100644 --- a/errors/codes/80016.md +++ b/errors/codes/80016.md @@ -1,5 +1,6 @@ --- code: 80016 +identifier: connection_replaced_by_a_newer_one title: Connection replaced by a newer one summary: An operation was attempted on a connection that was no longer current — a newer connection had replaced it, or its transport handle had been recycled — so the operation could not be applied and the client re-establishes to continue. --- diff --git a/errors/codes/80017.md b/errors/codes/80017.md index a7b53246..74ec4a49 100644 --- a/errors/codes/80017.md +++ b/errors/codes/80017.md @@ -1,5 +1,6 @@ --- code: 80017 +identifier: connection_closed title: Connection closed summary: The connection was closed deliberately, rather than dropped. This is the expected outcome when the connection is closed by request and is not a fault. --- diff --git a/errors/codes/80018.md b/errors/codes/80018.md index 4c62d3ba..b26a92c3 100644 --- a/errors/codes/80018.md +++ b/errors/codes/80018.md @@ -1,5 +1,6 @@ --- code: 80018 +identifier: invalid_connection_key title: Invalid connection key summary: A client tried to re-use a previous connection ID with a connection key, but the key was not in a valid format, so it could not keep that connection ID and was given a new one. --- diff --git a/errors/codes/80019.md b/errors/codes/80019.md index 52fc13fd..87f612a1 100644 --- a/errors/codes/80019.md +++ b/errors/codes/80019.md @@ -1,5 +1,6 @@ --- code: 80019 +identifier: token_request_failed title: Token request failed summary: The Ably SDK failed to retrieve a token from the configured authUrl or authCallback. --- diff --git a/errors/codes/80020.md b/errors/codes/80020.md index b536ef04..1595b319 100644 --- a/errors/codes/80020.md +++ b/errors/codes/80020.md @@ -1,5 +1,6 @@ --- code: 80020 +identifier: connection_discontinuity_message_rate_exceeded title: Continuity lost as message delivery rate exceeded summary: Messages were dropped because they were being delivered to the connection faster than its per-connection rate limit allows. Continuity is lost on the affected channels: they keep receiving new messages, but the dropped ones are not redelivered. --- diff --git a/errors/codes/80021.md b/errors/codes/80021.md index 07e42d07..04fd0175 100644 --- a/errors/codes/80021.md +++ b/errors/codes/80021.md @@ -1,5 +1,6 @@ --- code: 80021 +identifier: rate_limit_exceeded_account_connection_creation title: Account-wide connection creation rate exceeded summary: A connection was refused because new connections were being opened across the account faster than the permitted rate. The limit applies to the account as a whole rather than to any single connection. --- diff --git a/errors/codes/80022.md b/errors/codes/80022.md index a6987eee..bf23b8f9 100644 --- a/errors/codes/80022.md +++ b/errors/codes/80022.md @@ -1,5 +1,6 @@ --- code: 80022 +identifier: connection_not_found title: Connection not found summary: A request referred to a connection that the server could not find, so the exchange could not continue and the client is signalled to reconnect. This is a lookup failure, not a loss of message continuity. --- diff --git a/errors/codes/80023.md b/errors/codes/80023.md index 8f506f14..8548b058 100644 --- a/errors/codes/80023.md +++ b/errors/codes/80023.md @@ -1,5 +1,6 @@ --- code: 80023 +identifier: connection_re_established_in_a_different_region title: Connection re-established in a different region summary: The client reconnected to a different Ably region from the one it was using, so it could not keep its connection ID and was given a new one. --- diff --git a/errors/codes/80024.md b/errors/codes/80024.md index 12aa2d17..fbba59a9 100644 --- a/errors/codes/80024.md +++ b/errors/codes/80024.md @@ -1,5 +1,6 @@ --- code: 80024 +identifier: outdated_ably_sdk_version title: Outdated Ably SDK version summary: The connecting Ably SDK is an old version, and the way it connects to Ably is being retired. Connections from these SDK versions increasingly fail until they can no longer connect. --- diff --git a/errors/codes/80030.md b/errors/codes/80030.md index 828cc456..58e26065 100644 --- a/errors/codes/80030.md +++ b/errors/codes/80030.md @@ -1,5 +1,6 @@ --- code: 80030 +identifier: client_restriction_not_satisfied title: Connection failed due to a client restriction summary: The connection was refused because a restriction placed on the client was not met. Restrictions can limit which clients are allowed to connect based on conditions set for the application. --- diff --git a/errors/codes/90000.md b/errors/codes/90000.md index cfef5ae2..d2b3436d 100644 --- a/errors/codes/90000.md +++ b/errors/codes/90000.md @@ -1,5 +1,6 @@ --- code: 90000 +identifier: channel_operation_failed title: Channel operation failed summary: A channel operation, such as attaching, detaching, or publishing, could not be completed. --- diff --git a/errors/codes/90001.md b/errors/codes/90001.md index 2145bcdd..350365d4 100644 --- a/errors/codes/90001.md +++ b/errors/codes/90001.md @@ -1,5 +1,6 @@ --- code: 90001 +identifier: channel_operation_failed_invalid_channel_state title: Channel in an invalid state for the operation summary: A channel operation was attempted while the channel was not in a state that permits it, such as publishing or performing an action on a channel that is not currently attached. --- diff --git a/errors/codes/90002.md b/errors/codes/90002.md index fe792536..b8382797 100644 --- a/errors/codes/90002.md +++ b/errors/codes/90002.md @@ -1,5 +1,6 @@ --- code: 90002 +identifier: channel_history_could_not_be_retrieved title: Channel history could not be retrieved summary: A request for a channel's message history could not be completed, because the position it asked to read from could not be resolved — either the query was incomplete or the point requested is no longer retained. --- diff --git a/errors/codes/90003.md b/errors/codes/90003.md index 1631c548..f60b92c3 100644 --- a/errors/codes/90003.md +++ b/errors/codes/90003.md @@ -1,5 +1,6 @@ --- code: 90003 +identifier: channel_continuity_not_guaranteed title: Channel continuity not guaranteed summary: The client resumed a channel after being disconnected long enough that the point it had reached is no longer available to resume from. The channel reattaches from the earliest point still available, but continuity up to that point is not guaranteed. --- diff --git a/errors/codes/90004.md b/errors/codes/90004.md index 5695c59a..5fdbeb33 100644 --- a/errors/codes/90004.md +++ b/errors/codes/90004.md @@ -1,5 +1,6 @@ --- code: 90004 +identifier: channel_backlog_too_large title: Channel backlog too large summary: The client requested to resume or rewind a channel whose backlog held more messages than a single replay can deliver. The channel attaches, but the messages beyond that limit are not replayed. --- diff --git a/errors/codes/90005.md b/errors/codes/90005.md index bc3f64ff..f841ebae 100644 --- a/errors/codes/90005.md +++ b/errors/codes/90005.md @@ -1,5 +1,6 @@ --- code: 90005 +identifier: channel_resumed_in_a_different_region title: Channel resumed in a different region summary: The client reconnected to a different Ably region from the one it was using, so the position it resumed from could not be applied. The channel attaches at the current point, and no earlier messages are replayed. --- diff --git a/errors/codes/90006.md b/errors/codes/90006.md index 2a2f0361..42117d4a 100644 --- a/errors/codes/90006.md +++ b/errors/codes/90006.md @@ -1,5 +1,6 @@ --- code: 90006 +identifier: channel_history_request_had_no_limit title: Channel history request had no limit summary: A request for a channel's message history could not be served because it asked for the complete history with no limit on how many messages to return. --- diff --git a/errors/codes/90007.md b/errors/codes/90007.md index 5a76ced8..5698ddbd 100644 --- a/errors/codes/90007.md +++ b/errors/codes/90007.md @@ -1,5 +1,6 @@ --- code: 90007 +identifier: channel_operation_timed_out title: Channel operation timed out summary: A channel attach or detach did not complete within the expected time because no response was received from Ably. The operation may not have taken effect, and is usually the result of a transient interruption. --- diff --git a/errors/codes/90008.md b/errors/codes/90008.md index a36f88e1..2c73eae3 100644 --- a/errors/codes/90008.md +++ b/errors/codes/90008.md @@ -1,5 +1,6 @@ --- code: 90008 +identifier: attach_point_not_found title: Attach point too old for untilAttach history summary: A history request using untilAttach relies on the channel's attach point still being among recent messages. In this case it was not, so the request could not be served and no messages were returned. --- diff --git a/errors/codes/90009.md b/errors/codes/90009.md index efa9ca6c..d24be74b 100644 --- a/errors/codes/90009.md +++ b/errors/codes/90009.md @@ -1,5 +1,6 @@ --- code: 90009 +identifier: subscribe_mode_not_enabled title: Subscribe operation failed as subscribe mode not enabled summary: Messages could not be delivered to a subscriber because the channel had not requested the subscribe mode in its channel options. Messages are only delivered to clients that request that mode, so the listener never fires. --- diff --git a/errors/codes/90010.md b/errors/codes/90010.md index 7b32ee98..332779f3 100644 --- a/errors/codes/90010.md +++ b/errors/codes/90010.md @@ -1,5 +1,6 @@ --- code: 90010 +identifier: too_many_channels title: Too many channels summary: An operation was rejected because it would exceed the maximum number of channels permitted, either the channels attached to a single connection or those included in a single batch request. --- diff --git a/errors/codes/90021.md b/errors/codes/90021.md index 63ee3121..b18d8c72 100644 --- a/errors/codes/90021.md +++ b/errors/codes/90021.md @@ -1,5 +1,6 @@ --- code: 90021 +identifier: rate_limit_exceeded_account_channel_creation title: Account-wide channel creation rate exceeded summary: Requests were rejected because the rate at which new channels were being created across the account exceeded the permitted limit. The limit counts channels newly activated within a period, not those already active. --- diff --git a/errors/codes/91000.md b/errors/codes/91000.md index 0879398c..842ce112 100644 --- a/errors/codes/91000.md +++ b/errors/codes/91000.md @@ -1,5 +1,6 @@ --- code: 91000 +identifier: cannot_enter_presence_without_a_client_id title: Cannot enter presence without a clientId summary: A request to enter the presence set was rejected because the connection had no clientId. Presence members are identified by their clientId, so one is required to enter. --- diff --git a/errors/codes/91001.md b/errors/codes/91001.md index 4ee10e69..a1864855 100644 --- a/errors/codes/91001.md +++ b/errors/codes/91001.md @@ -1,5 +1,6 @@ --- code: 91001 +identifier: cannot_enter_presence_in_the_channels_current_state title: Cannot enter presence in the channel's current state summary: A request to enter the presence set was rejected because the channel was not in a state that permits presence operations, such as a channel that is detached, suspended, or failed. --- diff --git a/errors/codes/91002.md b/errors/codes/91002.md index a1a78378..5e030f0e 100644 --- a/errors/codes/91002.md +++ b/errors/codes/91002.md @@ -1,5 +1,6 @@ --- code: 91002 +identifier: cannot_leave_presence_that_was_never_entered title: Cannot leave presence that was never entered summary: A request to leave the presence set was rejected because the clientId was not a member of it. --- diff --git a/errors/codes/91003.md b/errors/codes/91003.md index 03087f34..e91a9ac6 100644 --- a/errors/codes/91003.md +++ b/errors/codes/91003.md @@ -1,5 +1,6 @@ --- code: 91003 +identifier: too_many_presence_members title: Too many presence members summary: A request to enter the presence set was rejected because the channel already held the maximum number of presence members permitted. The limit counts the members present on a single channel. --- diff --git a/errors/codes/91004.md b/errors/codes/91004.md index f13f4338..b73bbc8d 100644 --- a/errors/codes/91004.md +++ b/errors/codes/91004.md @@ -1,5 +1,6 @@ --- code: 91004 +identifier: unable_to_automatically_re_enter_presence title: Unable to automatically re-enter presence summary: After reconnecting, the Ably SDK tried to re-enter the presence set on the client's behalf but the attempt did not succeed, so the member was not restored to the presence set. --- diff --git a/errors/codes/91005.md b/errors/codes/91005.md index d9fa6dac..72411c3b 100644 --- a/errors/codes/91005.md +++ b/errors/codes/91005.md @@ -1,5 +1,6 @@ --- code: 91005 +identifier: presence_state_out_of_sync title: Presence state out of sync summary: The presence set held by the client no longer matched the state on Ably. The presence members are re-synchronised so the two are brought back into agreement. --- diff --git a/errors/codes/91006.md b/errors/codes/91006.md index ed48437d..c8330d0e 100644 --- a/errors/codes/91006.md +++ b/errors/codes/91006.md @@ -1,5 +1,6 @@ --- code: 91006 +identifier: total_presence_set_data_too_large title: Total presence set data too large summary: A request to enter the presence set was rejected because the combined size of the data held by members on the channel would exceed the maximum permitted for the presence set. --- diff --git a/errors/codes/91007.md b/errors/codes/91007.md index 0891d10f..5d2bdd79 100644 --- a/errors/codes/91007.md +++ b/errors/codes/91007.md @@ -1,5 +1,6 @@ --- code: 91007 +identifier: unexpected_loss_of_presence_membership title: Unexpected loss of presence membership summary: The connection was removed from the presence set despite still being active, so the channel was detached to prompt the Ably SDK to automatically re-enter presence. The condition is usually transient. --- diff --git a/errors/codes/91008.md b/errors/codes/91008.md index e7307ef4..8035d265 100644 --- a/errors/codes/91008.md +++ b/errors/codes/91008.md @@ -1,5 +1,6 @@ --- code: 91008 +identifier: presence_subscribe_mode_not_enabled title: Presence operation failed as presence_subscribe mode not enabled summary: The presence members of a channel could not be retrieved because the channel had not requested the presence_subscribe mode in its channel options. Presence state is only delivered to clients that request that mode. --- diff --git a/errors/codes/91100.md b/errors/codes/91100.md index 6eefd243..42b4406e 100644 --- a/errors/codes/91100.md +++ b/errors/codes/91100.md @@ -1,5 +1,6 @@ --- code: 91100 +identifier: presence_member_left_as_their_connection_closed title: Presence member left as their connection closed summary: A presence member was removed from the presence set because it was no longer present, usually because the connection it entered on was closed. --- diff --git a/errors/codes/92000.md b/errors/codes/92000.md index f2197679..c8b4355c 100644 --- a/errors/codes/92000.md +++ b/errors/codes/92000.md @@ -1,5 +1,6 @@ --- code: 92000 +identifier: invalid_object_message title: Invalid LiveObjects message summary: A LiveObjects message was rejected because it was invalid or did not conform to the expected structure. This usually indicates a problem with how the object operation was constructed before it was sent. --- diff --git a/errors/codes/92001.md b/errors/codes/92001.md index a2ad8f1b..1a7ee50d 100644 --- a/errors/codes/92001.md +++ b/errors/codes/92001.md @@ -1,5 +1,6 @@ --- code: 92001 +identifier: object_limit_exceeded title: LiveObjects limit exceeded summary: A LiveObjects operation was rejected because it would take the channel beyond the maximum number of objects it is allowed to hold. The limit is set by the account. --- diff --git a/errors/codes/92002.md b/errors/codes/92002.md index 59d0ba85..e4f5bc32 100644 --- a/errors/codes/92002.md +++ b/errors/codes/92002.md @@ -1,5 +1,6 @@ --- code: 92002 +identifier: operation_on_tombstone_object title: LiveObjects operation on a deleted object summary: An operation could not be applied because the target LiveObject had already been deleted. A deleted object is retained only as a marker, or tombstone, and can no longer be modified. --- diff --git a/errors/codes/92003.md b/errors/codes/92003.md index ac78a44c..a4d512f3 100644 --- a/errors/codes/92003.md +++ b/errors/codes/92003.md @@ -1,5 +1,6 @@ --- code: 92003 +identifier: object_root_deleted title: LiveObjects root has been deleted summary: A LiveObjects object tree could not be fetched because the object at its root had already been deleted. A deleted object is retained only as a marker, or tombstone, and cannot serve as the root of a tree. --- diff --git a/errors/codes/92004.md b/errors/codes/92004.md index 2030ba20..ef2ac30e 100644 --- a/errors/codes/92004.md +++ b/errors/codes/92004.md @@ -1,5 +1,6 @@ --- code: 92004 +identifier: object_not_found title: LiveObjects object not found summary: A LiveObjects operation referenced an object that does not exist on the channel. The object may never have been created, or it may have been removed before the operation was applied. --- diff --git a/errors/codes/92005.md b/errors/codes/92005.md index 4cdab935..b4e229da 100644 --- a/errors/codes/92005.md +++ b/errors/codes/92005.md @@ -1,5 +1,6 @@ --- code: 92005 +identifier: no_objects_at_path title: LiveObjects path matched no objects summary: A LiveObjects operation specified a path within an object tree that did not resolve to any object. The path may be incorrect, or the objects it points to may not exist. --- diff --git a/errors/codes/92006.md b/errors/codes/92006.md index 2f03ebf6..fa367a27 100644 --- a/errors/codes/92006.md +++ b/errors/codes/92006.md @@ -1,5 +1,6 @@ --- code: 92006 +identifier: object_operation_missing_identifier_or_path title: LiveObjects operation missing object reference summary: A LiveObjects operation could not be performed because it specified neither an object identifier nor a path. --- diff --git a/errors/codes/92007.md b/errors/codes/92007.md index 4d0bf5a2..2f79b835 100644 --- a/errors/codes/92007.md +++ b/errors/codes/92007.md @@ -1,5 +1,6 @@ --- code: 92007 +identifier: object_operation_path_not_processable title: LiveObjects operation not valid for the path summary: A LiveObjects operation could not be applied to the object at the specified path, because the operation is not compatible with the kind of object found there. --- diff --git a/errors/codes/92008.md b/errors/codes/92008.md index 218672a8..5ac3cb5c 100644 --- a/errors/codes/92008.md +++ b/errors/codes/92008.md @@ -1,5 +1,6 @@ --- code: 92008 +identifier: objects_sync_did_not_complete title: LiveObjects sync did not complete summary: A LiveObjects operation could not be applied because the channel had not finished synchronising its objects. --- diff --git a/errors/codes/93001.md b/errors/codes/93001.md index dcd9f0d7..56f5aa97 100644 --- a/errors/codes/93001.md +++ b/errors/codes/93001.md @@ -1,5 +1,6 @@ --- code: 93001 +identifier: annotation_subscribe_mode_not_enabled title: Annotation listener without annotation_subscribe mode summary: An annotation listener was added to a channel that had not requested the annotation_subscribe mode in its channel options. Annotations are only delivered to clients that explicitly request them, so the listener will not receive anything. --- diff --git a/errors/codes/93002.md b/errors/codes/93002.md index 95a5dc40..1158c6ce 100644 --- a/errors/codes/93002.md +++ b/errors/codes/93002.md @@ -1,5 +1,6 @@ --- code: 93002 +identifier: mutable_messages_not_enabled title: Message annotations, updates, appends, and deletes not enabled summary: An operation could not be performed because it requires a feature that is enabled by the channel namespace's 'Message annotations, updates, appends, and deletes' setting. --- diff --git a/errors/guidelines.md b/errors/guidelines.md index bae05c32..f6ea23dc 100644 --- a/errors/guidelines.md +++ b/errors/guidelines.md @@ -17,6 +17,7 @@ Each file is a Markdown document with YAML frontmatter: ```markdown --- code: 40142 +identifier: token_expired title: Token expired summary: The client's connection or request was rejected because the authentication token had expired. Each token has an expiry time that is set when it is issued. --- @@ -27,6 +28,7 @@ summary: The client's connection or request was rejected because the authenticat | Field | Required | Description | |---|---|---| | `code` | Yes | The error code. Must match the filename. | +| `identifier` | Yes | A stable `snake_case` name, unique across the registry, used as the canonical basis for the constant each SDK generates. See [Choosing an identifier](#choosing-an-identifier). | | `title` | Yes | A short noun phrase describing the error. See [Writing titles](#writing-titles). | | `summary` | Yes | A one or two sentence plain-prose summary. See [Writing summaries](#writing-summaries). | @@ -42,6 +44,16 @@ The same file feeds two surfaces: This is why the summary lives in frontmatter rather than as a body section: it has strict rules (length, plain prose, no formatting) and is reused across both views. +## Choosing an identifier + +The `identifier` is the machine-facing name for the code: SDKs generate a constant from it (transformed into each language's idiom — `SCREAMING_SNAKE`, `PascalCase`, and so on). Unlike the title, which is prose for humans and may be reworded, the identifier is a **stable contract** — once published, changing it breaks every SDK that generates from it, so choose it carefully and don't churn it. + +- **`snake_case`, matching `^[a-z][a-z0-9_]*$`**, and unique across the registry. +- **Name the cause, not the consequence.** For `80007` prefer `connection_message_limit_exceeded` over `connection_continuity_lost` — the limit is why it happened; the continuity loss is just the effect. +- **Concise but not cryptic.** Aim to keep it readable at a glance; avoid both cryptic abbreviations (`conn`, `creds`) and restating the whole title as a sentence. +- **Spell words out and use UK spelling**, consistent with the title and summary (`unrecognised`, not `unrecognized`). +- **Don't derive it mechanically from the title.** The title can change; the identifier can't. Set it deliberately. + ## Writing titles The title's job is to let a user decide, in under a second, whether this error is the one they care about. diff --git a/errors/scripts/validate-errors.js b/errors/scripts/validate-errors.js index 03957021..d1f4f561 100644 --- a/errors/scripts/validate-errors.js +++ b/errors/scripts/validate-errors.js @@ -10,14 +10,15 @@ * * Two tiers: * - Errors fail CI (exit 1): unparseable frontmatter, missing/blank `code`, - * `title`, or `summary`, filename/`code` mismatch, non-integer `code`, and - * any `errors.json` code with no `codes/.md` (completeness). + * `identifier`, `title`, or `summary`, filename/`code` mismatch, non-integer + * `code`, a malformed or duplicate `identifier`, and any `errors.json` code + * with no `codes/.md` (completeness). * - Warnings do not fail CI: a `codes/.md` whose code is absent from * `errors.json` (curated ahead of the map, or the map is being retired), * and soft length checks on `title`/`summary` per guidelines.md. * - * A valid registry entry requires `code`, `title`, and `summary` (per - * guidelines.md); a Markdown body is optional. + * A valid registry entry requires `code`, `identifier`, `title`, and `summary` + * (per guidelines.md); a Markdown body is optional. */ const fs = require('fs'); @@ -29,6 +30,7 @@ const ERRORS_JSON = path.resolve(ERRORS_DIR, '..', 'protocol', 'errors.json'); const errors = []; const warnings = []; +const identifiers = new Map(); const fail = (file, message) => errors.push(`${file}: ${message}`); const warn = (file, message) => warnings.push(`${file}: ${message}`); @@ -104,6 +106,18 @@ function validateFile(fileName) { fail(fileName, `\`code\` (${fields.code}) does not match filename`); } + // identifier — required; a stable snake_case name, unique across the registry, + // used as the canonical basis for the constant each SDK generates. + if (!fields.identifier || fields.identifier.trim() === '') { + fail(fileName, 'missing required `identifier` frontmatter field'); + } else if (!/^[a-z][a-z0-9_]*$/.test(fields.identifier)) { + fail(fileName, `\`identifier\` must match ^[a-z][a-z0-9_]*$, got "${fields.identifier}"`); + } else if (identifiers.has(fields.identifier)) { + fail(fileName, `\`identifier\` "${fields.identifier}" is already used by ${identifiers.get(fields.identifier)}`); + } else { + identifiers.set(fields.identifier, fileName); + } + // title if (!fields.title || fields.title.trim() === '') { fail(fileName, 'missing required `title` frontmatter field'); From dca1fe7c6d2dce565ff8394e38a1013e38abf0ab Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 11:36:28 +0100 Subject: [PATCH 5/9] Use US spelling across the error registry The Ably documentation style guide requires US spelling. Convert the UK spellings present in the code entries (behaviour, cancelled, favour, recognised, signalled, synchronised, unauthorised, unrecognised and their inflections) to US across titles, summaries, bodies, and identifiers, and update guidelines/CLAUDE to require US spelling. Co-Authored-By: Claude Opus 4.8 (1M context) --- errors/CLAUDE.md | 2 +- errors/codes/40002.md | 2 +- errors/codes/40005.md | 2 +- errors/codes/40032.md | 2 +- errors/codes/40033.md | 6 +++--- errors/codes/40100.md | 2 +- errors/codes/40130.md | 4 ++-- errors/codes/40143.md | 8 ++++---- errors/codes/42920.md | 2 +- errors/codes/42924.md | 2 +- errors/codes/70002.md | 2 +- errors/codes/80005.md | 2 +- errors/codes/80022.md | 2 +- errors/codes/80024.md | 2 +- errors/codes/91005.md | 2 +- errors/codes/92008.md | 2 +- errors/guidelines.md | 5 +++-- 17 files changed, 25 insertions(+), 24 deletions(-) diff --git a/errors/CLAUDE.md b/errors/CLAUDE.md index 0bee91b8..c597eae6 100644 --- a/errors/CLAUDE.md +++ b/errors/CLAUDE.md @@ -13,7 +13,7 @@ The set of files in `codes/` **is the registry** — the source of truth for whi These are reminders, not substitutes for `guidelines.md`. - **Filename = code.** `40142.md` must have `code: 40142` in frontmatter. -- **Identifier** — a stable `snake_case` name (`^[a-z][a-z0-9_]*$`), unique across the registry, the canonical basis for each SDK's generated constant. Name the cause not the consequence; concise but not cryptic; UK spelling. A frozen contract — don't churn it, and don't derive it mechanically from the title. +- **Identifier** — a stable `snake_case` name (`^[a-z][a-z0-9_]*$`), unique across the registry, the canonical basis for each SDK's generated constant. Name the cause not the consequence; concise but not cryptic; US spelling. A frozen contract — don't churn it, and don't derive it mechanically from the title. - **Title** — 3–7 words (cap 10), short phrase or concise subject–verb clause (not necessarily a noun phrase), sentence case, no trailing punctuation, no error-code restatement. Specific enough that a reader can distinguish it from neighbouring errors at a glance. - **Summary** — 15–40 words, one or two sentences, plain language. Describe what happened, then (when known) why it might have happened. Don't prescribe remediation — no "Check…", "Verify…", "Retry…". Generic phrasing — the summary represents *every* occurrence of the code, so avoid deictic ("this token", "your channel"); prefer "the token", "the channel". No Markdown formatting, stack traces, or internal identifiers. - **Body** (optional) — detail-page content beneath the title and summary. Don't restate either. Order: *What you should do* (lead with the triage — including when the answer is "nothing", for transient/self-healing errors) → *Why it happens* (causes in the reader's terms, each with its fix) → *What you'll see* (message string(s) and status, last, as a findability aid). Link out to feature docs for how-to; no inline code samples. No "related errors" section — disambiguate inline only when codes are genuinely confusable. Describe generically, instruct in second person. diff --git a/errors/codes/40002.md b/errors/codes/40002.md index 8ba66c91..978f0211 100644 --- a/errors/codes/40002.md +++ b/errors/codes/40002.md @@ -2,5 +2,5 @@ code: 40002 identifier: invalid_request_parameter_name title: Invalid request parameter name -summary: The request included a parameter that is not recognised or not permitted for the operation being performed. +summary: The request included a parameter that is not recognized or not permitted for the operation being performed. --- diff --git a/errors/codes/40005.md b/errors/codes/40005.md index 5e1d7a86..7e3e259e 100644 --- a/errors/codes/40005.md +++ b/errors/codes/40005.md @@ -2,5 +2,5 @@ code: 40005 identifier: invalid_credential title: Invalid API key or token -summary: An API key or token supplied with the request could not be read because it was not in the expected form. This is distinct from a key or token that is well-formed but unauthorised. +summary: An API key or token supplied with the request could not be read because it was not in the expected form. This is distinct from a key or token that is well-formed but unauthorized. --- diff --git a/errors/codes/40032.md b/errors/codes/40032.md index 4eea5f08..fe84aab8 100644 --- a/errors/codes/40032.md +++ b/errors/codes/40032.md @@ -2,5 +2,5 @@ code: 40032 identifier: invalid_message_extras_field title: Invalid message extras field -summary: A publish was rejected because a message included an extras field that is not permitted, or one whose value was the wrong type. Only recognised extras keys with values of the expected shape are accepted. +summary: A publish was rejected because a message included an extras field that is not permitted, or one whose value was the wrong type. Only recognized extras keys with values of the expected shape are accepted. --- diff --git a/errors/codes/40033.md b/errors/codes/40033.md index faef947e..b9ab52bc 100644 --- a/errors/codes/40033.md +++ b/errors/codes/40033.md @@ -1,7 +1,7 @@ --- code: 40033 -identifier: operation_cancelled -title: Operation cancelled +identifier: operation_canceled +title: Operation canceled summary: An operation was stopped before completing, so its result was discarded. This usually happens when the connection or request that triggered the operation ends first. --- @@ -15,4 +15,4 @@ The operation, such as publishing a message, was stopped part-way rather than re ## What you'll see -The error is reported with code 40033 and HTTP status 400. The message is `operation cancelled`; the spelling `operation canceled` appears on some paths. +The error is reported with code 40033 and HTTP status 400. The message is `operation canceled`; the spelling `operation canceled` appears on some paths. diff --git a/errors/codes/40100.md b/errors/codes/40100.md index 76aec7ad..bf7564ed 100644 --- a/errors/codes/40100.md +++ b/errors/codes/40100.md @@ -1,6 +1,6 @@ --- code: 40100 -identifier: unauthorised +identifier: unauthorized title: Unauthorized summary: A connection or request was rejected because it was not authorized. --- diff --git a/errors/codes/40130.md b/errors/codes/40130.md index 947ee9be..319edbea 100644 --- a/errors/codes/40130.md +++ b/errors/codes/40130.md @@ -1,6 +1,6 @@ --- code: 40130 identifier: key_error -title: API key not recognised -summary: Authentication failed because the API key presented was not recognised. This usually means the key does not exist or has been removed. +title: API key not recognized +summary: Authentication failed because the API key presented was not recognized. This usually means the key does not exist or has been removed. --- diff --git a/errors/codes/40143.md b/errors/codes/40143.md index d4effe73..473c7609 100644 --- a/errors/codes/40143.md +++ b/errors/codes/40143.md @@ -1,7 +1,7 @@ --- code: 40143 -identifier: token_unrecognised -title: Token not recognised +identifier: token_unrecognized +title: Token not recognized summary: A connection or request was rejected because Ably could not find a record of the token presented. Ably stores a record of certain tokens only while they remain valid, so this often means the token had already expired. --- @@ -13,8 +13,8 @@ Act when it persists or reaches your application. The common cause is a client r ## Why it happens -Ably keeps a stored record of some [tokens](https://ably.com/docs/auth/token), and only for as long as they remain valid. This error means a token of that kind was presented but no matching record was found, most often because it had expired and its record had already been removed. That is why such a token is reported as unrecognised rather than expired (40142). +Ably keeps a stored record of some [tokens](https://ably.com/docs/auth/token), and only for as long as they remain valid. This error means a token of that kind was presented but no matching record was found, most often because it had expired and its record had already been removed. That is why such a token is reported as unrecognized rather than expired (40142). ## What you'll see -The error is reported with code 40143 and HTTP status 401. The message is `token unrecognised`; some paths include the token identifier, as `Token unrecognised: `. +The error is reported with code 40143 and HTTP status 401. The message is `token unrecognized`; some paths include the token identifier, as `Token unrecognized: `. diff --git a/errors/codes/42920.md b/errors/codes/42920.md index 4cd4d2b8..dc545425 100644 --- a/errors/codes/42920.md +++ b/errors/codes/42920.md @@ -7,7 +7,7 @@ summary: The connection was closed because a rate limit was exceeded severely en ## What you should do -The error message names the metric for the limit involved. Reduce the rate of the operation it identifies before reconnecting, otherwise the connection will be closed again if the behaviour continues. +The error message names the metric for the limit involved. Reduce the rate of the operation it identifies before reconnecting, otherwise the connection will be closed again if the behavior continues. ## Why it happens diff --git a/errors/codes/42924.md b/errors/codes/42924.md index 9563be06..388b80c7 100644 --- a/errors/codes/42924.md +++ b/errors/codes/42924.md @@ -7,7 +7,7 @@ summary: A connection was terminated because the rate of inbound protocol messag ## What you should do -The threshold is high (around 2000 protocol messages per second), well above what the official Ably SDKs send, so reaching it usually indicates a client that is not using an official SDK and is sending far more protocol messages than intended. Look for a loop or bug in that client, such as repeatedly attaching and detaching a channel or publishing in a tight loop. The connection can be re-established, but it will be closed again if the behaviour continues. +The threshold is high (around 2000 protocol messages per second), well above what the official Ably SDKs send, so reaching it usually indicates a client that is not using an official SDK and is sending far more protocol messages than intended. Look for a loop or bug in that client, such as repeatedly attaching and detaching a channel or publishing in a tight loop. The connection can be re-established, but it will be closed again if the behavior continues. ## Why it happens diff --git a/errors/codes/70002.md b/errors/codes/70002.md index d6c3db0c..a226b860 100644 --- a/errors/codes/70002.md +++ b/errors/codes/70002.md @@ -7,7 +7,7 @@ summary: An integration invoked an HTTP target, such as a webhook, which replied ## What you should do -Determine the HTTP status code the target returned from the error message, and check the target's own logs, since it received the request and chose to reject it. A 4xx status points to the request being treated as malformed or unauthorised, so check any authentication, required headers, or payload shape the target enforces. A 5xx status points to a fault in the target itself. +Determine the HTTP status code the target returned from the error message, and check the target's own logs, since it received the request and chose to reject it. A 4xx status points to the request being treated as malformed or unauthorized, so check any authentication, required headers, or payload shape the target enforces. A 5xx status points to a fault in the target itself. To have a delivery counted as successful, the target must respond with a 2xx status. Ably retries server errors with a backoff, so a transient 5xx recovers on its own; repeated client errors will keep failing until the target accepts the request. diff --git a/errors/codes/80005.md b/errors/codes/80005.md index aa55b269..d4a7e0d5 100644 --- a/errors/codes/80005.md +++ b/errors/codes/80005.md @@ -2,5 +2,5 @@ code: 80005 identifier: connection_no_longer_available title: Connection no longer available -summary: A request referenced an existing connection, but the server no longer held its state — typically because the connection had been idle and was released after a period without activity. The client is signalled to reconnect and continue on a fresh connection. +summary: A request referenced an existing connection, but the server no longer held its state — typically because the connection had been idle and was released after a period without activity. The client is signaled to reconnect and continue on a fresh connection. --- diff --git a/errors/codes/80022.md b/errors/codes/80022.md index bf23b8f9..ab7e992b 100644 --- a/errors/codes/80022.md +++ b/errors/codes/80022.md @@ -2,5 +2,5 @@ code: 80022 identifier: connection_not_found title: Connection not found -summary: A request referred to a connection that the server could not find, so the exchange could not continue and the client is signalled to reconnect. This is a lookup failure, not a loss of message continuity. +summary: A request referred to a connection that the server could not find, so the exchange could not continue and the client is signaled to reconnect. This is a lookup failure, not a loss of message continuity. --- diff --git a/errors/codes/80024.md b/errors/codes/80024.md index fbba59a9..24a2a60b 100644 --- a/errors/codes/80024.md +++ b/errors/codes/80024.md @@ -11,7 +11,7 @@ Update the Ably SDK you are using to a supported version. See the [deprecation n ## Why it happens -Older versions of the Ably SDKs connect to Ably using an earlier version of the Ably protocol. Ably is retiring it in favour of a newer version that is more efficient and scalable and supports features the older one cannot. +Older versions of the Ably SDKs connect to Ably using an earlier version of the Ably protocol. Ably is retiring it in favor of a newer version that is more efficient and scalable and supports features the older one cannot. ## What you'll see diff --git a/errors/codes/91005.md b/errors/codes/91005.md index 72411c3b..baaa624b 100644 --- a/errors/codes/91005.md +++ b/errors/codes/91005.md @@ -2,5 +2,5 @@ code: 91005 identifier: presence_state_out_of_sync title: Presence state out of sync -summary: The presence set held by the client no longer matched the state on Ably. The presence members are re-synchronised so the two are brought back into agreement. +summary: The presence set held by the client no longer matched the state on Ably. The presence members are re-synchronized so the two are brought back into agreement. --- diff --git a/errors/codes/92008.md b/errors/codes/92008.md index 5ac3cb5c..889290be 100644 --- a/errors/codes/92008.md +++ b/errors/codes/92008.md @@ -2,5 +2,5 @@ code: 92008 identifier: objects_sync_did_not_complete title: LiveObjects sync did not complete -summary: A LiveObjects operation could not be applied because the channel had not finished synchronising its objects. +summary: A LiveObjects operation could not be applied because the channel had not finished synchronizing its objects. --- diff --git a/errors/guidelines.md b/errors/guidelines.md index f6ea23dc..68fc5f80 100644 --- a/errors/guidelines.md +++ b/errors/guidelines.md @@ -51,7 +51,7 @@ The `identifier` is the machine-facing name for the code: SDKs generate a consta - **`snake_case`, matching `^[a-z][a-z0-9_]*$`**, and unique across the registry. - **Name the cause, not the consequence.** For `80007` prefer `connection_message_limit_exceeded` over `connection_continuity_lost` — the limit is why it happened; the continuity loss is just the effect. - **Concise but not cryptic.** Aim to keep it readable at a glance; avoid both cryptic abbreviations (`conn`, `creds`) and restating the whole title as a sentence. -- **Spell words out and use UK spelling**, consistent with the title and summary (`unrecognised`, not `unrecognized`). +- **Spell words out and use US spelling**, consistent with the title and summary (`unrecognized`, not `unrecognised`). - **Don't derive it mechanically from the title.** The title can change; the identifier can't. Set it deliberately. ## Writing titles @@ -136,7 +136,8 @@ If you need a term that isn't covered here, check the dictionary before inventin ## Tone, across all fields -- **Plain, calm, and specific.** The reader is often stressed, in a hurry, or unfamiliar with the system. Optimise for them. +- **Plain, calm, and specific.** The reader is often stressed, in a hurry, or unfamiliar with the system. Optimize for them. +- **US spelling.** Per the Ably documentation style guide, use US spelling throughout — every field, including the body (`color`, `behavior`, `canceled`, `recognize`, `unauthorized`). - **Consistent voice across errors.** When 15 of these are read end-to-end, they should feel like they were written by one person. Inconsistency across teams is the most common failure mode here. - **Treat error copy as product copy, not log messages.** It's worth a second pair of eyes — engineers aren't always the best judges of what reads naturally to a user. From 803b52fc477387f79a138d803ec1c0478819051e Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 11:59:37 +0100 Subject: [PATCH 6/9] Add error codes 40128 and 40151 Sync two codes split out in the realtime repo: - 40128 (account application limit exceeded), split from 40115. - 40151 (token connection limit exceeded), split from 80019. 40115's meaning moved to 40128, so correct 40115 in errors.json to its current "account restricted (request limit exceeded)" meaning (the registry entry already reflected this). Both new codes get an identifier. Co-Authored-By: Claude Opus 4.8 (1M context) --- errors/codes/40128.md | 6 ++++++ errors/codes/40151.md | 6 ++++++ protocol/errors.json | 4 +++- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 errors/codes/40128.md create mode 100644 errors/codes/40151.md diff --git a/errors/codes/40128.md b/errors/codes/40128.md new file mode 100644 index 00000000..dd1e8a00 --- /dev/null +++ b/errors/codes/40128.md @@ -0,0 +1,6 @@ +--- +code: 40128 +identifier: account_application_limit_exceeded +title: Account application limit exceeded +summary: A new application could not be created because the account had reached the maximum number of applications it is permitted. +--- diff --git a/errors/codes/40151.md b/errors/codes/40151.md new file mode 100644 index 00000000..e82ed381 --- /dev/null +++ b/errors/codes/40151.md @@ -0,0 +1,6 @@ +--- +code: 40151 +identifier: token_connection_limit_exceeded +title: Token connection limit exceeded +summary: A connection was refused because the number of concurrent connections using the same token had reached the maximum permitted for a single token. +--- diff --git a/protocol/errors.json b/protocol/errors.json index 0ae74961..d3969a74 100644 --- a/protocol/errors.json +++ b/protocol/errors.json @@ -49,12 +49,13 @@ "40112": "account blocked (message limits exceeded)", "40113": "account blocked", "40114": "account restricted (channel limits exceeded)", - "40115": "maximum number of permitted applications exceeded", + "40115": "account restricted (request limit exceeded)", "40120": "application disabled", "40121": "token revocation not enabled for this application", "40125": "maximum number of rules per application exceeded", "40126": "maximum number of namespaces per application exceeded", "40127": "maximum number of keys per application exceeded", + "40128": "maximum number of permitted applications exceeded", "40130": "key error (unspecified)", "40131": "key revoked", "40132": "key expired", @@ -67,6 +68,7 @@ "40144": "invalid JWT format", "40145": "invalid token format", "40150": "connection blocked (limits exceeded)", + "40151": "connection blocked (max connections per token exceeded)", "40160": "operation not permitted with provided capability", "40161": "operation not permitted as it requires an identified client", "40162": "operation not permitted with a token, requires basic auth", From 66ac3e57d5fd8ad927e7f57595557da028c1e31e Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 12:29:22 +0100 Subject: [PATCH 7/9] Generate protocol/errors.json from the error registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make errors/codes/*.md the single source of truth and derive errors.json from it, eliminating the drift between the two (e.g. the 40115 wording that had gone stale in errors.json). errors.json changes shape from a flat code → title map to `{ codes: { "": { identifier, title, summary } } }`, now carrying the identifier and summary, not just a title. - errors/scripts/generate-errors-json.js writes errors.json from the registry (shared frontmatter parser with the validator so they can't diverge). - json-schemas/src/errors.json describes the new shape; validate:errors-json checks it. - CI regenerates and `git diff --exit-code`s errors.json as a drift guard, so it can't be hand-edited out of sync. - The validator drops its now-redundant errors.json cross-check. This changes the structure parsed by ably-go, ably-ruby and ably-ai-transport-js; their generators need updating to read the new shape and adopt `identifier` (a follow-up, per the error-docs plan). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/check.yml | 3 + errors/CLAUDE.md | 2 +- errors/README.md | 6 +- errors/guidelines.md | 2 +- errors/scripts/frontmatter.js | 42 + errors/scripts/generate-errors-json.js | 57 + errors/scripts/validate-errors.js | 81 +- json-schemas/src/errors.json | 45 + json-schemas/versions.json | 1 + package.json | 2 + protocol/README.md | 2 +- protocol/errors.json | 1554 +++++++++++++++++++----- 12 files changed, 1440 insertions(+), 357 deletions(-) create mode 100644 errors/scripts/frontmatter.js create mode 100644 errors/scripts/generate-errors-json.js create mode 100644 json-schemas/src/errors.json diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index ea832e06..0808a907 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -26,6 +26,9 @@ jobs: npm run lint npm run validate:agents npm run validate:errors + npm run validate:errors-json + npm run generate:errors + git diff --exit-code -- protocol/errors.json || { echo 'ERROR: protocol/errors.json is out of date. Run `npm run generate:errors` and commit the result.'; false; } npm test check-go: diff --git a/errors/CLAUDE.md b/errors/CLAUDE.md index c597eae6..313219d8 100644 --- a/errors/CLAUDE.md +++ b/errors/CLAUDE.md @@ -6,7 +6,7 @@ The per-code files live in [`codes/`](./codes): one Markdown file per error code, named `.md` (e.g. `40142.md`). Each file has YAML frontmatter (`code`, `identifier`, `title`, `summary`) and an optional Markdown body that provides the detail-page content beneath the title and summary. Title, summary, and body conventions are all covered in `guidelines.md`. -The set of files in `codes/` **is the registry** — the source of truth for which codes are valid and what they mean. `protocol/errors.json` is the legacy `code → title` map this registry supersedes; it is being retired. While it still exists, the validator (`scripts/`) checks that every `errors.json` code has a matching `codes/.md`, so keep the two aligned when adding a code. +The set of files in `codes/` **is the registry** — the source of truth for which codes are valid and what they mean. `protocol/errors.json` is generated from it (a machine-readable map of each code to its `identifier`, `title`, and `summary`); never edit it by hand — run `npm run generate:errors`, which CI enforces via a drift check. ## Conventions at a glance diff --git a/errors/README.md b/errors/README.md index 08f1fb13..0de1c119 100644 --- a/errors/README.md +++ b/errors/README.md @@ -20,9 +20,9 @@ summary: The channel name in the request was not valid, for example because it w - **`title`** — a short phrase identifying the error at a glance (aim for 3–7 words), e.g. `Invalid channel name`. - **`summary`** — one or two plain-language sentences (roughly 15–40 words) covering what happened and, when known, why. -You can add an optional Markdown body beneath the frontmatter for detail-page content (what to do, why it happens). While `protocol/errors.json` still exists, add the same code there too — the validator requires every `errors.json` code to have a registry file. +You can add an optional Markdown body beneath the frontmatter for detail-page content (what to do, why it happens). Don't touch `protocol/errors.json` — it is generated from these files; run `npm run generate:errors` to regenerate it after adding or editing a code. -See [`guidelines.md`](./guidelines.md) for the full rules on title, summary, body, tone, and terminology, and run `npm run validate:errors` to check your entry (CI runs it automatically). +See [`guidelines.md`](./guidelines.md) for the full rules on title, summary, body, tone, and terminology, and run `npm run validate:errors` to check your entry (CI runs both, and fails if `errors.json` is out of date). ## What's in this directory @@ -31,4 +31,4 @@ See [`guidelines.md`](./guidelines.md) for the full rules on title, summary, bod - [`CLAUDE.md`](./CLAUDE.md) — guidance for agents adding, editing, or reviewing entries. - [`scripts/`](./scripts) — the validator run in CI. -`protocol/errors.json` is the legacy `code → title` map that this registry supersedes; it is being retired once every consumer has moved off it. Until then, the validator checks that the registry and `errors.json` stay aligned. +`protocol/errors.json` is generated from this registry — a machine-readable map of each code to its `identifier`, `title`, and `summary`. It must not be edited by hand; run `npm run generate:errors` to regenerate it, and CI fails if the committed file is out of date. diff --git a/errors/guidelines.md b/errors/guidelines.md index 68fc5f80..5f9fe702 100644 --- a/errors/guidelines.md +++ b/errors/guidelines.md @@ -111,7 +111,7 @@ Use this small, consistent set of sections. Use only the ones that apply, and om - **Don't inline code samples.** Examples of how to use a feature belong in that feature's documentation, where a customer can find them *before* they hit an error — link there instead. If there is no doc to link to, that is a general documentation gap: raise it (e.g. open an issue) rather than papering over it with a one-off snippet here. - **Link out for how-to; keep the page about the error.** The body explains the error and points at the fix; it is not a tutorial. Link to maintained values (limits, durations) rather than restating them, so the page can't drift. - **Weave links into the prose.** Link the words that name the thing — "enable [persistence](...)", "[around two minutes](...)" — rather than appending a separate "(see ...)" aside. The sentence should still read naturally if the link markup were stripped out. -- **Verify facts against their source; don't cite it.** Check message strings, status codes, and limits against the raising code or `errors.json` before writing them — never from memory. Those are how you verify, not what you link: the page links only to customer-facing docs. Confirm those links resolve. +- **Verify facts against their source; don't cite it.** Check message strings, status codes, and limits against the raising code before writing them — never from memory. Those are how you verify, not what you link: the page links only to customer-facing docs. Confirm those links resolve. - **Tight, not exhaustive.** Include only what serves the decision and the fix. A good body is often three or four short paragraphs. - **Terminology and tone** follow the same rules as every other field — use the [Dictionary of terms](https://ably.atlassian.net/wiki/spaces/devex/pages/4295262228/Dictionary+of+terms), and keep the voice plain and calm. diff --git a/errors/scripts/frontmatter.js b/errors/scripts/frontmatter.js new file mode 100644 index 00000000..dda0be3b --- /dev/null +++ b/errors/scripts/frontmatter.js @@ -0,0 +1,42 @@ +/** + * Shared parser for the constrained YAML frontmatter used by `codes/.md`. + * Used by both the validator and the errors.json generator so they can't + * diverge in how they read a file. + */ + +/** + * Parse the frontmatter: a `---` fence, then one `key: value` per line, then a + * closing `---`. Values may contain colons (we split on the first `: ` only) + * and may be wrapped in double quotes. + * + * @param {string} content - The full file contents. + * @returns {{ fields: object, hasBody: boolean } | { error: string }} Parsed + * frontmatter fields and whether a body follows, or an error description. + */ +function parseFrontmatter(content) { + if (!content.startsWith('---\n')) { + return { error: 'missing opening `---` frontmatter fence' }; + } + const rest = content.slice(4); + const end = rest.indexOf('\n---'); + if (end === -1) { + return { error: 'missing closing `---` frontmatter fence' }; + } + const block = rest.slice(0, end); + const body = rest.slice(end + 4).replace(/^\n+/, ''); + const fields = {}; + block.split('\n').forEach((raw) => { + const line = raw.trimEnd(); + if (line === '') return; + const sep = line.indexOf(': '); + const key = sep === -1 ? line.replace(/:$/, '') : line.slice(0, sep); + let value = sep === -1 ? '' : line.slice(sep + 2); + if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) { + value = value.slice(1, -1).replace(/\\"/g, '"'); + } + fields[key.trim()] = value; + }); + return { fields, hasBody: body.trim().length > 0 }; +} + +module.exports = { parseFrontmatter }; diff --git a/errors/scripts/generate-errors-json.js b/errors/scripts/generate-errors-json.js new file mode 100644 index 00000000..a7d1f33e --- /dev/null +++ b/errors/scripts/generate-errors-json.js @@ -0,0 +1,57 @@ +#!/usr/bin/env node + +/* + * Generates `protocol/errors.json` from the error registry in `errors/codes/`. + * + * The registry (`codes/.md`) is the source of truth; errors.json is a + * derived, machine-readable view of it — a map of code to `{ identifier, title, + * summary }`. Do not edit errors.json by hand: run `npm run generate:errors`. + * CI regenerates it and fails if the committed file differs (the drift guard). + */ + +const fs = require('fs'); +const path = require('path'); +const { parseFrontmatter } = require('./frontmatter'); + +const CODES_DIR = path.resolve(__dirname, '..', 'codes'); +const OUT = path.resolve(__dirname, '..', '..', 'protocol', 'errors.json'); + +const REQUIRED = ['code', 'identifier', 'title', 'summary']; + +/** + * Build the errors.json object from the registry files. + * + * @returns {object} The generated structure: `{ $schema, _comment, codes }`. + */ +function build() { + const files = fs.readdirSync(CODES_DIR).filter((f) => f.endsWith('.md')); + const codes = {}; + files + .map((f) => f.replace(/\.md$/, '')) + .sort((a, b) => Number(a) - Number(b)) + .forEach((code) => { + const content = fs.readFileSync(path.join(CODES_DIR, `${code}.md`), 'utf8'); + const parsed = parseFrontmatter(content); + if (parsed.error) { + throw new Error(`${code}.md: ${parsed.error}`); + } + const { fields } = parsed; + const missing = REQUIRED.filter((k) => !fields[k]); + if (missing.length) { + throw new Error(`${code}.md: missing frontmatter field(s): ${missing.join(', ')}`); + } + codes[code] = { + identifier: fields.identifier, + title: fields.title, + summary: fields.summary, + }; + }); + return { + $schema: '../json-schemas/src/errors.json', + _comment: 'Generated from errors/codes/*.md by errors/scripts/generate-errors-json.js. Do not edit by hand; create and update files in errors/codes/*.md and run `npm run generate:errors`.', + codes, + }; +} + +fs.writeFileSync(OUT, `${JSON.stringify(build(), null, 2)}\n`); +console.log(`Wrote ${path.relative(process.cwd(), OUT)}`); diff --git a/errors/scripts/validate-errors.js b/errors/scripts/validate-errors.js index d1f4f561..af6115fe 100644 --- a/errors/scripts/validate-errors.js +++ b/errors/scripts/validate-errors.js @@ -5,17 +5,16 @@ * * The set of `.md` files in `errors/codes/` is the source of truth for * which error codes are valid. This script enforces the invariants that must - * hold for every file, and cross-checks completeness against the legacy - * `protocol/errors.json` map while that still exists. + * hold for every file. `protocol/errors.json` is generated from these files + * (see generate-errors-json.js), so it isn't cross-checked here — the CI + * drift guard (regenerate and diff) keeps it in sync. * * Two tiers: * - Errors fail CI (exit 1): unparseable frontmatter, missing/blank `code`, * `identifier`, `title`, or `summary`, filename/`code` mismatch, non-integer - * `code`, a malformed or duplicate `identifier`, and any `errors.json` code - * with no `codes/.md` (completeness). - * - Warnings do not fail CI: a `codes/.md` whose code is absent from - * `errors.json` (curated ahead of the map, or the map is being retired), - * and soft length checks on `title`/`summary` per guidelines.md. + * `code`, and a malformed or duplicate `identifier`. + * - Warnings do not fail CI: soft length checks on `title`/`summary` per + * guidelines.md. * * A valid registry entry requires `code`, `identifier`, `title`, and `summary` * (per guidelines.md); a Markdown body is optional. @@ -23,10 +22,10 @@ const fs = require('fs'); const path = require('path'); +const { parseFrontmatter } = require('./frontmatter'); const ERRORS_DIR = path.resolve(__dirname, '..'); const CODES_DIR = path.join(ERRORS_DIR, 'codes'); -const ERRORS_JSON = path.resolve(ERRORS_DIR, '..', 'protocol', 'errors.json'); const errors = []; const warnings = []; @@ -35,41 +34,6 @@ const identifiers = new Map(); const fail = (file, message) => errors.push(`${file}: ${message}`); const warn = (file, message) => warnings.push(`${file}: ${message}`); -/** - * Parse the constrained YAML frontmatter used by these files: a `---` fence, - * then one `key: value` per line, then a closing `---`. Values may contain - * colons (we split on the first `: ` only) and may be wrapped in double quotes. - * - * @param {string} content - The full file contents. - * @returns {{ fields: object, hasBody: boolean } | { error: string }} Parsed - * frontmatter fields and whether a body follows, or an error description. - */ -function parseFrontmatter(content) { - if (!content.startsWith('---\n')) { - return { error: 'missing opening `---` frontmatter fence' }; - } - const rest = content.slice(4); - const end = rest.indexOf('\n---'); - if (end === -1) { - return { error: 'missing closing `---` frontmatter fence' }; - } - const block = rest.slice(0, end); - const body = rest.slice(end + 4).replace(/^\n+/, ''); - const fields = {}; - block.split('\n').forEach((raw) => { - const line = raw.trimEnd(); - if (line === '') return; - const sep = line.indexOf(': '); - const key = sep === -1 ? line.replace(/:$/, '') : line.slice(0, sep); - let value = sep === -1 ? '' : line.slice(sep + 2); - if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) { - value = value.slice(1, -1).replace(/\\"/g, '"'); - } - fields[key.trim()] = value; - }); - return { fields, hasBody: body.trim().length > 0 }; -} - /** * Count whitespace-separated words in a string. * @@ -152,33 +116,8 @@ function main() { } const files = fs.readdirSync(CODES_DIR).filter((f) => f.endsWith('.md')); - const present = new Set(); - files.forEach((file) => { - const code = validateFile(file); - if (code) present.add(code); - }); - - // Completeness + reverse cross-check against the legacy errors.json map. - if (fs.existsSync(ERRORS_JSON)) { - const map = JSON.parse(fs.readFileSync(ERRORS_JSON, 'utf8')); - const jsonCodes = new Set(Object.keys(map)); - - [...jsonCodes] - .filter((c) => !present.has(c)) - .sort((a, b) => a - b) - .forEach((code) => { - fail('registry', `errors.json defines ${code} ("${map[code]}") but codes/${code}.md is missing`); - }); - - [...present] - .filter((c) => !jsonCodes.has(c)) - .sort((a, b) => a - b) - .forEach((code) => { - warn(`codes/${code}.md`, 'code is not present in protocol/errors.json'); - }); - } else { - warn('registry', 'protocol/errors.json not found; skipping completeness cross-check'); - } + files.forEach(validateFile); + const codeCount = files.length; if (warnings.length) { console.warn(`\n${warnings.length} warning(s):`); @@ -190,7 +129,7 @@ function main() { console.error('\nError registry validation failed.'); process.exit(1); } - console.log(`\n✓ Error registry valid: ${present.size} code(s) in errors/codes/.`); + console.log(`\n✓ Error registry valid: ${codeCount} code(s) in errors/codes/.`); } main(); diff --git a/json-schemas/src/errors.json b/json-schemas/src/errors.json new file mode 100644 index 00000000..cb08487f --- /dev/null +++ b/json-schemas/src/errors.json @@ -0,0 +1,45 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Ably Error Codes", + "description": "The registry of Ably error codes, generated from errors/codes/*.md in ably-common.", + "type": "object", + "required": ["codes"], + "additionalProperties": false, + "properties": { + "$schema": { + "type": "string", + "description": "The JSON Schema reference." + }, + "_comment": { + "type": "string", + "description": "Note that this file is generated; not part of the data." + }, + "codes": { + "type": "object", + "description": "A map of error code to its definition.", + "additionalProperties": false, + "patternProperties": { + "^[0-9]+$": { + "type": "object", + "required": ["identifier", "title", "summary"], + "additionalProperties": false, + "properties": { + "identifier": { + "type": "string", + "description": "A stable snake_case name for the code, the canonical basis for the constant each SDK generates.", + "pattern": "^[a-z][a-z0-9_]*$" + }, + "title": { + "type": "string", + "description": "A short human-readable title for the error." + }, + "summary": { + "type": "string", + "description": "A one or two sentence plain-language summary of the error." + } + } + } + } + } + } +} diff --git a/json-schemas/versions.json b/json-schemas/versions.json index 79154556..629be159 100644 --- a/json-schemas/versions.json +++ b/json-schemas/versions.json @@ -1,5 +1,6 @@ { "agents": "0.0.2", + "errors": "0.0.1", "account-stats": "0.0.3", "app-stats": "0.0.5", "client-events-api-requests": "0.0.1", diff --git a/package.json b/package.json index fa32a4a3..ee5ea37c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "test": "jest", "validate:agents": "node scripts/validate-json-schema.js protocol/agents.json", "validate:errors": "node errors/scripts/validate-errors.js", + "validate:errors-json": "node scripts/validate-json-schema.js protocol/errors.json", + "generate:errors": "node errors/scripts/generate-errors-json.js && prettier --write protocol/errors.json", "validate:json-schema": "node scripts/validate-json-schema.js", "fetch:agent-releases": "node scripts/fetch-agent-releases.js", "export:agents": "node scripts/export-agents-csv.js", diff --git a/protocol/README.md b/protocol/README.md index da586dc6..f036e291 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -2,7 +2,7 @@ ## Error Codes -Ably's [client library SDKs](https://www.ably.io/download), and the [core realtime platform](https://www.ably.io/platform), use common error codes. These error codes are stored and maintained in the shared public JSON file [errors.json](./errors.json). +Ably's [client library SDKs](https://www.ably.io/download), and the [core realtime platform](https://www.ably.io/platform), use common error codes. The canonical registry of these codes is maintained as one Markdown file per code in [`errors/codes`](../errors); the machine-readable [errors.json](./errors.json) in this directory — a map of each code to its `identifier`, `title` and `summary` — is generated from that registry (via `npm run generate:errors`) and must not be edited by hand. In addition, a list of error codes and corresponding support articles to help understand how to resolve common problems relating to these error codes can be seen in [errorsHelp.json](./errorsHelp.json). This ensures that with every Ably error message that includes a help link for the error in the format `https://help.ably.io/error/{{ERROR_CODE}}`, the user is taken to a relevant FAQ if it exists, and if one does not exist, we record the number of times that error code has been visited so that the docs/support team can work on adding relevant documentation. diff --git a/protocol/errors.json b/protocol/errors.json index d3969a74..48d0976f 100644 --- a/protocol/errors.json +++ b/protocol/errors.json @@ -1,282 +1,1276 @@ { - "10000": "no error", - - "20000": "general error code", - - "40000": "bad request", - "40001": "invalid request body", - "40002": "invalid parameter name", - "40003": "invalid parameter value", - "40004": "invalid header", - "40005": "invalid credential", - "40006": "invalid connection id", - "40007": "invalid message id", - "40008": "invalid content length", - "40009": "maximum message length exceeded", - "40010": "invalid channel name", - "40011": "stale ring state", - "40012": "invalid client id", - "40013": "Invalid message data or encoding", - "40014": "Resource disposed", - "40015": "Invalid device id", - "40016": "Invalid message name", - "40017": "Unsupported protocol version", - "40018": "Unable to decode message; channel attachment no longer viable", - "40019": "Required client library plugin not present", - "40020": "Batch error", - "40021": "Feature requires a newer platform version", - "40022": "API Streamer has been shut down", - "40023": "Action requires a newer protocol version", - "40024": "unable to get channel history (incompatible site)", - "40025": "API no longer supported", - "40030": "Invalid publish request (unspecified)", - "40031": "Invalid publish request (invalid client-specified id)", - "40032": "Invalid publish request (impermissible extras field)", - "40033": "Operation cancelled", - "40034": "Invalid publish request (maximum number of messages per ChannelMessage exceeded)", - "40035": "integration rule messageFilter regex must be RE2-compatible; backreferences and lookaround aren't supported", - "40099": "Reserved for artifical errors for testing", - - "40100": "unauthorized", - "40101": "invalid credentials", - "40102": "incompatible credentials", - "40103": "invalid use of Basic auth over non-TLS transport", - "40104": "timestamp not current", - "40105": "nonce value replayed", - "40106": "Unable to obtain credentials from given parameters", - "40110": "account disabled", - "40111": "account restricted (connection limits exceeded)", - "40112": "account blocked (message limits exceeded)", - "40113": "account blocked", - "40114": "account restricted (channel limits exceeded)", - "40115": "account restricted (request limit exceeded)", - "40120": "application disabled", - "40121": "token revocation not enabled for this application", - "40125": "maximum number of rules per application exceeded", - "40126": "maximum number of namespaces per application exceeded", - "40127": "maximum number of keys per application exceeded", - "40128": "maximum number of permitted applications exceeded", - "40130": "key error (unspecified)", - "40131": "key revoked", - "40132": "key expired", - "40133": "key disabled", - "40133": "wrong key; cannot revoke tokens with a different key to the one that issued them", - "40140": "token error (unspecified)", - "40141": "token revoked", - "40142": "token expired", - "40143": "token unrecognised", - "40144": "invalid JWT format", - "40145": "invalid token format", - "40150": "connection blocked (limits exceeded)", - "40151": "connection blocked (max connections per token exceeded)", - "40160": "operation not permitted with provided capability", - "40161": "operation not permitted as it requires an identified client", - "40162": "operation not permitted with a token, requires basic auth", - "40163": "operation not permitted, key not marked as permitting revocable tokens", - "40164": "channel state disabled", - "40165": "channel mode not requested when attaching", - "40166": "unidentified client cannot modify own messages", - "40170": "error from client token callback", - "40171": "no means provided to renew auth token", - "40172": "operation only permitted with token auth", - "40180": "unable to send location notification; location notifications can only be sent using token authentication", - "40181": "unable to send location notification; no location token for device", - - "40300": "forbidden", - "40310": "account does not permit tls connection", - "40311": "operation requires tls connection", - "40320": "application requires authentication", - "40330": "unable to activate account due to placement constraint (unspecified)", - "40331": "unable to activate account due to placement constraint (incompatible environment)", - "40332": "unable to activate account due to placement constraint (incompatible site)", - - "40400": "not found", - - "40500": "method not allowed", - - "40900": "conflict", - - "41001": "push device registration expired", - - "42200": "unprocessable content", - "42210": "content rejected (unspecified)", - "42211": "content rejected by before publish integration", - "42212": "content rejected by validation", - "42213": "content rejected by moderation", - - "42910": "System rate limit exceeded", - "42911": "Per-connection publish rate exceeded", - "42912": "there is a channel iteration call already in progress; only 1 active call is permitted to execute at any one time", - "42913": "Per-channel publish rate exceeded", - "42914": "Per-channel publish bandwidth exceeded", - "42915": "Per-connection outbound message rate exceeded", - "42916": "Per-connection outbound resume rate exceeded", - "42917": "Account-wide message publish rate exceeded", - "42918": "Account-wide API request rate exceeded", - "42920": "rate limit exceeded (fatal)", - "42921": "Connection terminated (per-connection publish rate exceeded)", - "42922": "Too many requests", - "42923": "Integration target responded with rate limit error", - "42924": "Per-connection protocol message rate exceeded", - "42925": "Account-wide integration invocation rate exceeded", - "42926": "Account-wide push notification rate exceeded", - - "50000": "internal error", - "50001": "internal channel error", - "50002": "internal connection error", - "50003": "timeout error", - "50004": "Request failed due to overloaded instance", - "50005": "Service unavailable (service temporarily in lockdown)", - "50006": "Connection superseded by a newer connection", - - "50010": "Ably's edge proxy service has encountered an unknown internal error whilst processing the request", - "50210": "Ably's edge proxy service received an invalid (bad gateway) response from the Ably platform", - "50310": "Ably's edge proxy service received a service unavailable response code from the Ably platform", - - "50320": "Active Traffic Management: traffic for this cluster is being temporarily redirected to a backup service", - - "50330": "request reached the wrong cluster; retry (used during dns changes for cluster migrations)", - - "50410": "Ably's edge proxy service timed out waiting for the Ably platform", - - "70000": "reactor operation failed", - "70001": "reactor operation failed (post operation failed)", - "70002": "reactor operation failed (post operation returned unexpected code)", - "70003": "reactor operation failed (maximum number of concurrent in-flight requests exceeded)", - "70004": "reactor operation failed (invalid or unaccepted message contents)", - "70005": "unable to deliver AMQP message (unspecified timeout)", - "70006": "unable to deliver AMQP message (cluster busy)", - - "71000": "Exchange error (unspecified)", - "71001": "Forced re-attachment due to permissions change", - "71100": "Exchange publisher error (unspecified)", - "71101": "No such publisher", - "71102": "Publisher not enabled as an exchange publisher", - "71200": "Exchange product error (unspecified)", - "71201": "No such product", - "71202": "Product disabled", - "71203": "No such channel in this product", - "71204": "Forced re-attachment due to product being remapped to a different namespace", - "71300": "Exchange subscription error (unspecified)", - "71301": "Subscription disabled", - "71302": "Requester has no subscription to this product", - "71303": "Channel does not match the channel filter specified in the subscription to this product", - - "72000": "ingress operation failed", - "72001": "ingress failed to publish message", - "72002": "ingress table is unhealthy", - "72003": "ingress cannot connect to database", - "72004": "ingress cannot identify channel, no _ablyChannel field", - "72005": "ingress invalid pipeline", - "72006": "unable to resume from change stream", - "72007": "unable to store change stream resume token", - "72008": "change stream history lost", - - "80000": "connection failed", - "80001": "connection failed (no compatible transport)", - "80002": "connection suspended", - "80003": "disconnected", - "80004": "already connected", - "80005": "invalid connection id (remote not found)", - "80006": "unable to recover connection (messages expired)", - "80007": "unable to recover connection (message limit exceeded)", - "80008": "unable to recover connection (connection expired)", - "80009": "connection not established (no transport handle)", - "80010": "invalid operation (invalid transport handle)", - "80011": "unable to recover connection (incompatible auth params)", - "80012": "unable to recover connection (invalid or unspecified connection serial)", - "80013": "protocol error", - "80014": "connection timed out", - "80015": "incompatible connection parameters", - "80016": "operation on superseded connection", - "80017": "connection closed", - "80018": "invalid connection id (invalid format)", - "80019": "client configured authentication provider request failed", - "80020": "continuity loss due to maximum subscribe message rate exceeded", - "80021": "exceeded maximum permitted account-wide rate of creating new connections", - "80022": "unable to continue current comet connection", - "80023": "unable to resume connection from a different site", - "80024": "protocol version is no longer supported", - "80030": "client restriction not satisfied", - - "90000": "channel operation failed", - "90001": "channel operation failed (invalid channel state)", - "90002": "channel operation failed (epoch expired or never existed)", - "90003": "unable to recover channel (messages expired)", - "90004": "unable to recover channel (message limit exceeded)", - "90005": "unable to recover channel (no matching epoch)", - "90006": "unable to recover channel (unbounded request)", - "90007": "channel operation failed (no response from server)", - "90008": "unable to get channel history (attach point not found)", - "90009": "messages not delivered to subscriber (channel attached without the subscribe mode)", - "90010": "maximum number of channels per connection/request exceeded", - "90021": "exceeded maximum permitted account-wide rate of creating new channels", - "91000": "unable to enter presence channel (no clientId)", - "91001": "unable to enter presence channel (invalid channel state)", - "91002": "unable to leave presence channel that is not entered", - "91003": "unable to enter presence channel (maximum member limit exceeded)", - "91004": "unable to automatically re-enter presence channel", - "91005": "presence state is out of sync", - "91006": "unable to enter presence channel (maximum presence set data size exceeded)", - "91007": "Unexpected loss of presence membership", - "91008": "unable to retrieve presence members (channel attached without the presence_subscribe mode)", - "91100": "member implicitly left presence channel (connection closed)", - - "92000": "invalid object message", - "92001": "objects limit exceeded", - "92002": "unable to submit operation on tombstone object", - "92003": "unable to fetch object tree with tombstone object as root", - "92004": "object not found", - "92005": "no objects found matching operation path", - "92006": "unable to perform operation without objectId or path", - "92007": "operation not processable on path", - "92008": "unable to apply objects operation; objects sync did not complete", - - "93001": "attempt to add an annotation listener without having requested the annotation_subscribe channel mode in ChannelOptions, which won't do anything (we only deliver annotations to clients who have explicitly requested them)", - "93002": "unable to perform operation; this operation can only be performed on channel in a namespace with Mutable Messages enabled", - - "101000": "must have a non-empty name for the space", - "101001": "must enter a space to perform this operation", - "101002": "lock request already exists", - "101003": "lock is currently locked", - "101004": "lock was invalidated by a concurrent lock request which now holds the lock", - - "102000": "chat room name is invalid", - "102001": "room attachment failed (messages)", - "102002": "room attachment failed (presence)", - "102003": "room attachment failed (reactions)", - "102004": "room attachment failed (occupancy)", - "102005": "room attachment failed (typing)", - "102050": "room detachment failed (messages)", - "102051": "room detachment failed (presence)", - "102052": "room detachment failed (reactions)", - "102053": "room detachment failed (occupancy)", - "102054": "room detachment failed (typing)", - "102100": "room discontinuity", - "102101": "unable to perform operation; room is in failed state", - "102102": "unable to perform operation; room is releasing", - "102103": "unable to perform operation; room is released", - "102104": "unable to perform operation; existing attempt failed", - "102105": "unknown room lifecycle error", - "102106": "room was released before the operation could complete", - "102107": "a room already exists with different options", - "102108": "feature is not enabled in room options", - "102109": "listener has not been subscribed yet", - "102110": "channel serial is not defined when expected", - "102111": "channel options cannot be modified after the channel has been requested", - "102112": "cannot perform operation because the room is in an invalid state", - "102113": "failed to enforce sequential execution of the operation", - "102200": "react hook must be used within the appropriate provider", - "102201": "react component has been unmounted", - "102202": "failed to fetch presence data after maximum retries", - - "103000": "Push notification internal error", - "103001": "Push notification retry limit reached", - "103002": "Push notification recipient missing", - "103003": "Push notification body invalid", - "103004": "Push notification rejected by provider", - "103005": "Push device token invalid", - "103006": "Push transport not configured", - "103007": "Push notification provider unreachable", - "103008": "Push transport credentials invalid" + "$schema": "../json-schemas/src/errors.json", + "_comment": "Generated from errors/codes/*.md by errors/scripts/generate-errors-json.js. Do not edit by hand; create and update files in errors/codes/*.md and run `npm run generate:errors`.", + "codes": { + "10000": { + "identifier": "no_error", + "title": "No error", + "summary": "A placeholder code indicating success, used where an error code is expected but the operation completed normally. It does not represent a failure." + }, + "20000": { + "identifier": "general_error", + "title": "General error", + "summary": "A generic error that does not correspond to a more specific code. It is used as a catch-all when the condition could not be classified more precisely." + }, + "40000": { + "identifier": "bad_request", + "title": "Bad request", + "summary": "The request was rejected because it was invalid and could not be processed." + }, + "40001": { + "identifier": "invalid_request_body", + "title": "Invalid request body", + "summary": "The body of the request could not be processed because it was invalid, missing required fields, or not in the expected format." + }, + "40002": { + "identifier": "invalid_request_parameter_name", + "title": "Invalid request parameter name", + "summary": "The request included a parameter that is not recognized or not permitted for the operation being performed." + }, + "40003": { + "identifier": "invalid_parameter_value", + "title": "Invalid request parameter value", + "summary": "A parameter in the request was invalid, such as a value of the wrong type, outside the allowed range, or otherwise unacceptable for that parameter." + }, + "40004": { + "identifier": "invalid_request_header", + "title": "Invalid request header", + "summary": "A header supplied with the request was missing, invalid, or held a value that was not accepted for the operation." + }, + "40005": { + "identifier": "invalid_credential", + "title": "Invalid API key or token", + "summary": "An API key or token supplied with the request could not be read because it was not in the expected form. This is distinct from a key or token that is well-formed but unauthorized." + }, + "40006": { + "identifier": "message_contains_invalid_connection_id", + "title": "Message contains invalid connection ID", + "summary": "A published message referenced a connection by an ID or key that was invalid or did not match, so the message was rejected." + }, + "40007": { + "identifier": "invalid_connection_serial", + "title": "Invalid connection serial", + "summary": "An older Ably SDK attempted to resume a connection using an invalid connection serial." + }, + "40008": { + "identifier": "invalid_content_length", + "title": "Invalid content length", + "summary": "More data arrived in the request body than its Content-Length header declared, so the request was rejected." + }, + "40009": { + "identifier": "max_message_size_exceeded", + "title": "Message too large", + "summary": "A message was rejected because it exceeded the maximum permitted size. When several messages are published in a single call, the limit applies to their combined size rather than to each one individually." + }, + "40010": { + "identifier": "invalid_channel_name", + "title": "Invalid channel name", + "summary": "The channel name in the request was not valid, for example because it was empty, contained characters that are not permitted, or used an unknown square-bracketed prefix." + }, + "40011": { + "identifier": "pagination_sequence_no_longer_valid", + "title": "Pagination sequence no longer valid", + "summary": "An enumeration query could not continue because the underlying set of results shifted between pages, so the pagination sequence was no longer valid." + }, + "40012": { + "identifier": "invalid_client_id", + "title": "Invalid client ID", + "summary": "The client ID supplied was not acceptable, for example because it was empty, a wildcard, not a string, or did not match the client ID permitted by the credentials in use." + }, + "40013": { + "identifier": "invalid_data_or_encoding", + "title": "Invalid message data or encoding", + "summary": "A message was rejected because its data was of an unsupported type, or because the encoding declared for the data could not be applied or reversed." + }, + "40014": { + "identifier": "resource_disposed", + "title": "Resource disposed", + "summary": "The operation could not complete because an internal Ably resource it targeted was temporarily unavailable, typically during routine changes on Ably's side such as failover or rebalancing." + }, + "40015": { + "identifier": "invalid_device_id", + "title": "Invalid device ID", + "summary": "The device ID supplied for a push notification operation was missing or not in an acceptable form, so the request was rejected." + }, + "40016": { + "identifier": "invalid_message_name", + "title": "Invalid message name", + "summary": "A message supplied a name that was invalid, so the message was rejected." + }, + "40017": { + "identifier": "unsupported_protocol_version", + "title": "Unsupported protocol version", + "summary": "The request specified a protocol version that Ably does not support, or omitted a version where one is required. The connection or request was rejected before processing." + }, + "40018": { + "identifier": "delta_decoding_failed", + "title": "Message delta could not be applied", + "summary": "On a channel using delta compression, the Ably SDK could not apply a message's delta against the preceding message — for example because that message was missed or arrived out of order. The SDK recovers automatically by reattaching the channel to receive a full message." + }, + "40019": { + "identifier": "missing_plugin", + "title": "Required SDK plugin not present", + "summary": "The operation needed an optional Ably SDK plugin that was not installed or registered, so it could not be carried out. Some features are provided by plugins that must be added alongside the core SDK." + }, + "40020": { + "identifier": "batch_request_error", + "title": "Batch request error", + "summary": "A batch request completed with one or more of its items failing. Each failing item carries its own error, reported alongside this one." + }, + "40021": { + "identifier": "feature_requires_a_newer_platform_version", + "title": "Feature requires a newer platform version", + "summary": "The requested feature is not available on the platform version in use. It relies on capabilities added in a later version than the one handling the request." + }, + "40022": { + "identifier": "api_streamer_no_longer_offered", + "title": "API Streamer no longer offered", + "summary": "The request targeted API Streamer, which has been shut down and is no longer available. Requests that depend on it can no longer be served." + }, + "40023": { + "identifier": "operation_requires_a_newer_sdk_version", + "title": "Operation requires a newer SDK version", + "summary": "The requested operation is not supported by the Ably SDK version in use, because it relies on a capability available only in a more recent version." + }, + "40024": { + "identifier": "incompatible_site_for_history", + "title": "untilAttach history request not served by attach region", + "summary": "A history request using untilAttach was handled by a different Ably region from the one the channel attached in. The attach point is specific to that region, so the request could not be served and no messages were returned." + }, + "40025": { + "identifier": "api_no_longer_supported", + "title": "API no longer supported", + "summary": "A method or feature that Ably no longer supports was called. It has been removed or replaced by a newer API, so the call is rejected instead of being performed." + }, + "40030": { + "identifier": "invalid_publish_request", + "title": "Invalid publish request", + "summary": "A publish request was rejected because it was invalid." + }, + "40031": { + "identifier": "invalid_client_specified_message_id", + "title": "Invalid client-specified message id", + "summary": "A publish was rejected because a client-supplied message id was missing, empty, or not in the required format. When several messages are published together, every message must carry a valid id following the expected pattern." + }, + "40032": { + "identifier": "invalid_message_extras_field", + "title": "Invalid message extras field", + "summary": "A publish was rejected because a message included an extras field that is not permitted, or one whose value was the wrong type. Only recognized extras keys with values of the expected shape are accepted." + }, + "40033": { + "identifier": "operation_canceled", + "title": "Operation canceled", + "summary": "An operation was stopped before completing, so its result was discarded. This usually happens when the connection or request that triggered the operation ends first." + }, + "40034": { + "identifier": "too_many_messages_in_one_publish", + "title": "Too many messages in one publish", + "summary": "A publish was rejected because it contained more messages than the maximum permitted count. This is about the number of messages, not their combined size, which is limited separately." + }, + "40035": { + "identifier": "integration_message_filter_regex_not_re2_compatible", + "title": "Integration message filter regex not RE2-compatible", + "summary": "An integration's message filter could not be applied because its regular expression is not compatible with the RE2 syntax. Backreferences and lookaround are not supported." + }, + "40099": { + "identifier": "reserved_for_testing", + "title": "Reserved for testing", + "summary": "This code is reserved for artificial errors produced during testing and does not indicate a genuine fault. It is not expected to appear during normal operation." + }, + "40100": { + "identifier": "unauthorized", + "title": "Unauthorized", + "summary": "A connection or request was rejected because it was not authorized." + }, + "40101": { + "identifier": "invalid_credentials", + "title": "Authentication failed", + "summary": "The credentials presented were not accepted — for example an API key secret that did not match, an invalid token, or no credentials supplied at all." + }, + "40102": { + "identifier": "incompatible_credentials", + "title": "Incompatible credentials", + "summary": "The credentials presented were valid but did not match the request — for example the client ID they permit differed from the one in use, or they belonged to a different application than the connection being resumed." + }, + "40103": { + "identifier": "invalid_use_of_basic_auth_over_non_tls_transport", + "title": "Basic authentication used over an insecure connection", + "summary": "Basic authentication was attempted over a connection that was not secured with TLS. Basic authentication sends the API key directly, so it is only permitted over an encrypted transport." + }, + "40104": { + "identifier": "token_request_timestamp_outside_permitted_window", + "title": "Token request timestamp outside permitted window", + "summary": "The timestamp in a token request fell outside the window Ably accepts, so the request was rejected. This usually happens when the clock of the system that generated the token request differs significantly from real time." + }, + "40105": { + "identifier": "nonce_value_replayed", + "title": "Nonce value replayed", + "summary": "A token request was rejected because its nonce had already been used. Each nonce may be presented only once, so a repeated value is treated as a replayed request." + }, + "40106": { + "identifier": "no_valid_authentication_method_provided", + "title": "No valid authentication method provided", + "summary": "The Ably SDK was given no usable way to authenticate — no API key, token, or token-request mechanism such as authCallback or authUrl was provided." + }, + "40110": { + "identifier": "account_disabled", + "title": "Account disabled", + "summary": "The request was rejected because the Ably account it belongs to is disabled. While an account is disabled, its applications cannot authenticate or carry traffic." + }, + "40111": { + "identifier": "account_connection_limit_exceeded", + "title": "Account connection limit exceeded", + "summary": "A new connection was refused because the account had reached the maximum number of concurrent connections permitted by its limits." + }, + "40112": { + "identifier": "account_message_limit_exceeded", + "title": "Account message limit exceeded", + "summary": "The request was rejected because the account had reached the maximum message volume permitted by its limits." + }, + "40113": { + "identifier": "account_blocked", + "title": "Account blocked", + "summary": "The request was rejected because the Ably account it belongs to is blocked." + }, + "40114": { + "identifier": "account_channel_limit_exceeded", + "title": "Account channel limit exceeded", + "summary": "The request was refused because the account had reached the maximum number of concurrent channels permitted by its limits." + }, + "40115": { + "identifier": "account_request_limit_exceeded", + "title": "Account request limit exceeded", + "summary": "The request was refused because the account had reached its permitted limit on API and token requests." + }, + "40120": { + "identifier": "application_disabled", + "title": "Application disabled", + "summary": "The request was rejected because the application it targets is disabled. While an application is disabled, it cannot authenticate or carry traffic." + }, + "40121": { + "identifier": "token_revocation_not_enabled", + "title": "Token revocation not enabled", + "summary": "A token revocation request was rejected because the revocation capability it requires is not enabled — either token revocation for the application, or revocation by channel for the account." + }, + "40125": { + "identifier": "application_integration_limit_exceeded", + "title": "Application integration limit exceeded", + "summary": "An integration could not be created because the application had reached the maximum number of integrations permitted by its limits." + }, + "40126": { + "identifier": "application_channel_rule_limit_exceeded", + "title": "Application channel rule limit exceeded", + "summary": "A channel rule could not be created because the application had reached the maximum number of channel rules permitted by its limits." + }, + "40127": { + "identifier": "application_api_key_limit_exceeded", + "title": "Application API key limit exceeded", + "summary": "A new API key could not be created because the application had reached the maximum number of keys permitted by its limits." + }, + "40128": { + "identifier": "account_application_limit_exceeded", + "title": "Account application limit exceeded", + "summary": "A new application could not be created because the account had reached the maximum number of applications it is permitted." + }, + "40130": { + "identifier": "key_error", + "title": "API key not recognized", + "summary": "Authentication failed because the API key presented was not recognized. This usually means the key does not exist or has been removed." + }, + "40131": { + "identifier": "key_revoked", + "title": "API key revoked", + "summary": "A connection or request was rejected because the API key it used has been revoked. A revoked key is permanently invalidated and can no longer authenticate." + }, + "40132": { + "identifier": "key_expired", + "title": "API key expired", + "summary": "A connection or request was rejected because the API key it used has expired. Keys can be given an expiry time, after which they can no longer authenticate." + }, + "40133": { + "identifier": "wrong_api_key_for_token_revocation", + "title": "Wrong API key for token revocation", + "summary": "A token revocation request was rejected because it was made with a different API key from the one that issued the tokens. Tokens can only be revoked using the key that issued them." + }, + "40140": { + "identifier": "token_error_unspecified", + "title": "Token not accepted", + "summary": "The authentication token was rejected, so the connection or request could not be authenticated. This error prompts the client to obtain a new token and retry." + }, + "40141": { + "identifier": "token_revoked", + "title": "Token revoked", + "summary": "A connection or request was rejected because the authentication token it used has been revoked. A revoked token is invalidated before its normal expiry and can no longer authenticate." + }, + "40142": { + "identifier": "token_expired", + "title": "Token expired", + "summary": "The client's connection or request was rejected because the authentication token had expired. Each token has an expiry time that is set when it is issued." + }, + "40143": { + "identifier": "token_unrecognized", + "title": "Token not recognized", + "summary": "A connection or request was rejected because Ably could not find a record of the token presented. Ably stores a record of certain tokens only while they remain valid, so this often means the token had already expired." + }, + "40144": { + "identifier": "invalid_jwt", + "title": "Invalid JWT token", + "summary": "A JWT presented for authentication could not be parsed. The token was not well-formed, so it could not be read as a valid JSON Web Token." + }, + "40145": { + "identifier": "invalid_ably_token", + "title": "Invalid Ably token", + "summary": "An Ably token presented for authentication could not be parsed, so its contents could not be read." + }, + "40150": { + "identifier": "application_connection_limit_exceeded", + "title": "Application connection limit exceeded", + "summary": "A new connection was refused because the application had reached the maximum number of concurrent connections permitted for it." + }, + "40151": { + "identifier": "token_connection_limit_exceeded", + "title": "Token connection limit exceeded", + "summary": "A connection was refused because the number of concurrent connections using the same token had reached the maximum permitted for a single token." + }, + "40160": { + "identifier": "capability_denied", + "title": "Client lacks the required capability", + "summary": "The API key or token used by the client isn't assigned the capability required for the operation — for example publishing, subscribing, or reading history on a channel, or listing channels and connections." + }, + "40161": { + "identifier": "identified_client_required", + "title": "Operation requires an identified client", + "summary": "The client had no established client ID, so an operation that requires one was refused." + }, + "40162": { + "identifier": "operation_requires_basic_authentication", + "title": "Operation requires Basic authentication", + "summary": "The operation was refused because it can only be performed with Basic authentication using an API key, but the client authenticated with a token. Token revocation is one such operation, which must use the key that issued the tokens." + }, + "40163": { + "identifier": "token_revocation_not_enabled_for_api_key", + "title": "Token revocation not enabled for API key", + "summary": "The operation was refused because the API key used does not have revocable tokens enabled. Revocation applies only to tokens issued by a key with that setting turned on." + }, + "40164": { + "identifier": "token_revocation_not_enabled_for_api_key_40164", + "title": "Token revocation not enabled for API key", + "summary": "The operation was refused because the API key used does not have revocable tokens enabled. Revocation applies only to tokens issued by a key with that setting turned on." + }, + "40165": { + "identifier": "channel_mode_not_requested_when_attaching", + "title": "Channel mode not requested when attaching", + "summary": "Publishing a message, object, or annotation — or entering presence — was rejected because the client did not request the matching channel mode when it attached. The credentials permit the operation; the mode that enables it was not requested." + }, + "40166": { + "identifier": "unidentified_client_cannot_modify_own_messages", + "title": "Unidentified client cannot modify own messages", + "summary": "A message update or delete was rejected because the client is unidentified — it has no clientId — but holds only the \"own\" form of the relevant capability, message-update-own or message-delete-own, which can never apply to a client that has no messages of its own." + }, + "40170": { + "identifier": "error_from_client_token_callback", + "title": "Token callback failed", + "summary": "Authentication could not complete because the client's own token-request mechanism, its authCallback or authUrl, returned an error instead of a token or token request. The failure originates in the application's auth logic or endpoint, not within Ably." + }, + "40171": { + "identifier": "token_renewal_not_configured", + "title": "Token renewal not configured", + "summary": "The auth token expired and could not be renewed because no renewal mechanism was configured. Without an authCallback, authUrl, or API key, the Ably SDK has no way to obtain a replacement token." + }, + "40172": { + "identifier": "operation_requires_token_authentication", + "title": "Operation requires Token authentication", + "summary": "The operation was refused because it can only be performed with Token authentication, but the client authenticated using Basic authentication. Some operations are available only to clients using a token." + }, + "40180": { + "identifier": "apns_token_authentication_required", + "title": "APNs token authentication required", + "summary": "A location or live-activity notification was not sent because these require the app's APNs credentials to use token-based authentication, but the app is not configured that way." + }, + "40181": { + "identifier": "no_location_token_for_device", + "title": "No location token for device", + "summary": "A location push notification was not sent to a device because the device has no location token registered. Location notifications can only be delivered to devices that have supplied one." + }, + "40300": { + "identifier": "forbidden", + "title": "Forbidden", + "summary": "The request was refused because it is not permitted. This covers a range of forbidden conditions, such as a disabled account or application, or a caller that lacks permission for the operation." + }, + "40310": { + "identifier": "account_does_not_permit_tls_connections", + "title": "Account does not permit TLS connections", + "summary": "The connection was rejected because it used TLS, but the account is configured not to allow TLS connections." + }, + "40311": { + "identifier": "application_requires_a_tls_connection", + "title": "Application requires a TLS connection", + "summary": "The connection was rejected because it did not use TLS, but the application requires TLS connections." + }, + "40320": { + "identifier": "authentication_required", + "title": "Authentication required", + "summary": "The request was rejected because it carried no authentication credentials." + }, + "40330": { + "identifier": "account_not_permitted_in_cluster_or_region", + "title": "Account not permitted in this cluster or region", + "summary": "The request reached a cluster or region the account is not permitted to use; the accompanying error message gives the specific reason." + }, + "40331": { + "identifier": "account_not_permitted_in_cluster", + "title": "Account not permitted in this cluster", + "summary": "The request reached an Ably cluster the account is not permitted to use." + }, + "40332": { + "identifier": "account_not_permitted_in_region", + "title": "Account not permitted in this region", + "summary": "The request reached a region the account is not permitted to use." + }, + "40400": { + "identifier": "not_found", + "title": "Resource not found", + "summary": "The requested resource does not exist, often because it has been deleted or the path or identifier used to reference it is incorrect." + }, + "40500": { + "identifier": "method_not_allowed", + "title": "HTTP method not allowed", + "summary": "The request used an HTTP method that the endpoint does not support." + }, + "40900": { + "identifier": "conflict", + "title": "Conflict", + "summary": "The request conflicted with the current state of the target, so it could not be completed." + }, + "41001": { + "identifier": "push_device_registration_expired", + "title": "Push device registration expired", + "summary": "The push notification could not be delivered because the target device's registration is no longer valid. Registrations expire over time, and the device must register again before it can receive notifications." + }, + "42200": { + "identifier": "unprocessable_content", + "title": "Message content could not be processed", + "summary": "A message was rejected because a content check — for example validation or moderation — did not accept it, rather than for a syntax or size problem." + }, + "42210": { + "identifier": "content_rejected", + "title": "Message content rejected", + "summary": "A message was rejected by a content check — such as validation, moderation, or an integration — but the error does not identify which one." + }, + "42211": { + "identifier": "content_rejected_by_before_publish_integration", + "title": "Message rejected by integration", + "summary": "A message was rejected by an integration configured to check messages before they are accepted." + }, + "42212": { + "identifier": "content_rejected_by_validation", + "title": "Message rejected by content validation", + "summary": "A message was rejected because it failed a content-validation check." + }, + "42213": { + "identifier": "content_rejected_by_moderation", + "title": "Message rejected by moderation", + "summary": "A message was rejected by a content-moderation check configured on the channel. The moderation check inspected the content and flagged it as not permitted." + }, + "42910": { + "identifier": "rate_limit_exceeded_generic", + "title": "System rate limit exceeded", + "summary": "An operation was rejected because a system rate limit was exceeded. The limit protects Ably against overload; the operation can be retried." + }, + "42911": { + "identifier": "rate_limit_exceeded_per_connection_inbound", + "title": "Per-connection publish rate exceeded", + "summary": "A message was rejected because the connection published messages faster than the per-connection publish rate limit allows. Publishing far above the limit can escalate to the connection being closed." + }, + "42912": { + "identifier": "rate_limit_channel_iteration_in_progress", + "title": "Channel enumeration already in progress", + "summary": "A request to enumerate the channels active in an app was rejected because another enumeration was already running. Only one channel enumeration can run at a time for an app." + }, + "42913": { + "identifier": "rate_limit_exceeded_per_channel_inbound", + "title": "Per-channel publish rate exceeded", + "summary": "A message was rejected because the rate of messages published to the channel exceeded its configured limit. The limit applies to all publishers to the channel combined, so it can be reached even when no single publisher is publishing quickly." + }, + "42914": { + "identifier": "rate_limit_exceeded_per_channel_bandwidth", + "title": "Per-channel publish bandwidth exceeded", + "summary": "A message was rejected because the rate of data published to the channel exceeded its configured bandwidth limit. The limit applies to all publishers to the channel combined, so large messages can reach it even at a low message rate." + }, + "42915": { + "identifier": "rate_limit_exceeded_per_connection_outbound", + "title": "Per-connection outbound message rate exceeded", + "summary": "Messages were not delivered to a connection because the rate of messages being sent to it exceeded its per-connection outbound limit. The client is notified of the gap by a channel continuity loss, which it receives as error 80020." + }, + "42916": { + "identifier": "rate_limit_exceeded_per_connection_backlog", + "title": "Per-connection outbound resume rate exceeded", + "summary": "Backlog messages were not delivered to a connection because the rate of delivering them exceeded its per-connection outbound limit. The client is notified of the gap by a channel continuity loss, which it receives as error 80020." + }, + "42917": { + "identifier": "rate_limit_exceeded_account_messages", + "title": "Account-wide message publish rate exceeded", + "summary": "A message was rejected because the rate of messages published across the account exceeded the configured account-wide limit. A proportion of messages are rejected to bring the account back within its limit." + }, + "42918": { + "identifier": "rate_limit_exceeded_account_api_requests", + "title": "Account-wide API request rate exceeded", + "summary": "A REST API request was rejected because the request rate across the account exceeded the configured account-wide limit. A proportion of requests are rejected to bring the account back within its limit." + }, + "42920": { + "identifier": "rate_limit_exceeded_connection_fatal", + "title": "Connection terminated by a fatal rate limit", + "summary": "The connection was closed because a rate limit was exceeded severely enough to be treated as fatal, rather than rejecting individual operations. This is the general form of a fatal rate-limit close; common cases have their own codes." + }, + "42921": { + "identifier": "rate_limit_exceeded_per_connection_inbound_fatal", + "title": "Connection terminated for far exceeding the per-connection publish rate", + "summary": "The connection was closed because it published far above the per-connection publish rate limit — around 20 times the permitted rate. Breaches below that reject individual messages without closing the connection." + }, + "42922": { + "identifier": "rate_limit_exceeded_too_many_requests", + "title": "Too many requests", + "summary": "A request was blocked because too many requests were received in a short period, triggering Ably's flood protection. This protects the service against excessive or abusive traffic, separately from your account's own rate limits." + }, + "42923": { + "identifier": "integration_target_rate_limit_response", + "title": "Integration target responded with rate limit error", + "summary": "An integration target such as an HTTP endpoint, serverless function, or message queue returned a rate-limit response when Ably invoked it. The limit is the target's own, not one of Ably's." + }, + "42924": { + "identifier": "rate_limit_exceeded_protocol_message_rate_fatal", + "title": "Per-connection protocol message rate exceeded", + "summary": "A connection was terminated because the rate of inbound protocol messages from the connection exceeded its configured per-connection limit. Protocol messages include message publishes, presence updates, attach/detach requests, and other client-initiated actions on the connection." + }, + "42925": { + "identifier": "rate_limit_exceeded_account_integrations", + "title": "Account-wide integration invocation rate exceeded", + "summary": "An integration invocation was dropped because the invocation rate across the account exceeded the configured account-wide limit. A proportion of invocations are dropped to bring the account back within its limit." + }, + "42926": { + "identifier": "rate_limit_exceeded_account_push_notifications", + "title": "Account-wide push notification rate exceeded", + "summary": "A push notification was dropped because the publish rate of push notifications across the account exceeded the configured account-wide limit. A proportion of push notifications are dropped to bring the account back within its limit." + }, + "50000": { + "identifier": "internal_error", + "title": "Internal server error", + "summary": "The request could not be completed because of an unexpected error inside the Ably platform." + }, + "50001": { + "identifier": "internal_channel_error", + "title": "Internal channel error", + "summary": "An operation on a channel failed because of an unexpected error inside the Ably platform." + }, + "50002": { + "identifier": "internal_connection_error", + "title": "Internal connection error", + "summary": "An operation on a connection failed because of an unexpected error inside the Ably platform." + }, + "50003": { + "identifier": "timeout_error", + "title": "Request timed out server-side", + "summary": "The Ably platform did not finish handling the request within the time allowed, so the request was abandoned. This usually reflects a transient server-side delay rather than a problem with the request." + }, + "50004": { + "identifier": "server_temporarily_busy", + "title": "Server temporarily busy", + "summary": "The request was rejected because the server handling it could not accept it at that moment. The condition is transient." + }, + "50005": { + "identifier": "service_temporarily_unavailable", + "title": "Service temporarily unavailable", + "summary": "The request could not be served because the service was temporarily in a locked-down state, during which requests are not accepted. The condition is temporary and expected to clear." + }, + "50006": { + "identifier": "connection_superseded_by_a_newer_connection", + "title": "Connection superseded by a newer connection", + "summary": "The connection's state was taken over by a more recent connection, typically a reconnection or resume. This only arises on a deprecated Ably protocol version; upgrade to a recent Ably SDK release to avoid it." + }, + "50010": { + "identifier": "edge_proxy_internal_error", + "title": "Edge proxy internal error", + "summary": "Ably's edge proxy service encountered an unknown internal error while handling the request and could not complete it." + }, + "50210": { + "identifier": "invalid_platform_response", + "title": "Invalid response from the platform", + "summary": "Ably's edge proxy service received an invalid response from the Ably platform behind it and could not complete the request. The condition is usually transient." + }, + "50310": { + "identifier": "platform_temporarily_unavailable", + "title": "Platform temporarily unavailable", + "summary": "Ably's edge proxy service received a service-unavailable response from the Ably platform behind it and could not complete the request. The condition is usually transient." + }, + "50320": { + "identifier": "traffic_temporarily_redirected", + "title": "Traffic temporarily redirected", + "summary": "Ably's Active Traffic Management temporarily redirected traffic away from its normal path, and the request was affected while that redirection was in place. The condition is temporary." + }, + "50330": { + "identifier": "request_reached_the_wrong_cluster", + "title": "Request reached the wrong cluster", + "summary": "The request arrived at a cluster that was not the one meant to handle it and should be retried. This can happen briefly during DNS changes while traffic is being migrated between clusters." + }, + "50410": { + "identifier": "edge_proxy_timed_out_waiting_for_platform", + "title": "Edge proxy timed out waiting for platform", + "summary": "Ably's edge proxy service did not receive a response from the Ably platform behind it within the time allowed, so the request was abandoned. The condition is usually transient." + }, + "70000": { + "identifier": "integration_operation_failed", + "title": "Integration operation failed", + "summary": "An integration could not complete an operation against its configured target." + }, + "70001": { + "identifier": "integration_invocation_failed", + "title": "Integration invocation failed", + "summary": "An integration could not invoke its configured target: Ably either could not reach the target, or reached it but could not deliver the data. The specific failure depends on the integration type and is named in the error message." + }, + "70002": { + "identifier": "integration_target_returned_an_error_status", + "title": "Integration target returned an error status", + "summary": "An integration invoked an HTTP target, such as a webhook, which replied with an HTTP status outside the successful 2xx range. Ably treats any non-2xx response as a failed delivery." + }, + "70003": { + "identifier": "too_many_concurrent_integration_requests", + "title": "Too many concurrent integration requests", + "summary": "An integration was not invoked because the number of requests already in flight for it had reached the permitted maximum. This usually happens when a target responds more slowly than the rate at which matching messages arrive." + }, + "70004": { + "identifier": "integration_refused_to_process_a_message", + "title": "Integration refused to process a message", + "summary": "An integration reached its configured target, but the target did not accept the message because its contents were invalid or unsupported for that target type." + }, + "70005": { + "identifier": "amqp_message_delivery_timed_out", + "title": "AMQP message delivery timed out", + "summary": "A message could not be delivered to an Ably message queue integration because the delivery did not complete within the allowed time. This usually points to the queue being unavailable or slow to accept the message." + }, + "70006": { + "identifier": "amqp_message_queue_busy", + "title": "AMQP message queue busy", + "summary": "A message could not be delivered to an Ably message queue integration because the message queue was too busy to accept it. This is usually transient and eases as load falls." + }, + "71000": { + "identifier": "exchange_error", + "title": "Exchange error", + "summary": "A request involving Ably Exchange failed for a reason that could not be attributed to a more specific cause. Exchange lets a publisher expose channels as a product that subscribers can receive." + }, + "71001": { + "identifier": "forced_re_attachment_after_permissions_change", + "title": "Forced re-attachment after permissions change", + "summary": "An Exchange channel was detached and re-attached because the permissions governing access to it changed, so the existing attachment no longer reflected what the client was entitled to receive." + }, + "71100": { + "identifier": "exchange_publisher_error", + "title": "Exchange publisher error", + "summary": "A request involving an Exchange publisher failed for a reason that could not be attributed to a more specific cause. A publisher is the party that exposes channels as an Exchange product." + }, + "71101": { + "identifier": "publisher_not_found", + "title": "Publisher not found", + "summary": "The Exchange request referred to a publisher that does not exist. This usually means the publisher identifier was incorrect or the publisher is no longer available." + }, + "71102": { + "identifier": "publisher_not_enabled_for_exchange", + "title": "Publisher not enabled for Exchange", + "summary": "The request referred to a party that exists but has not been enabled as an Exchange publisher, so it cannot expose channels as a product for subscribers to receive." + }, + "71200": { + "identifier": "exchange_product_error", + "title": "Exchange product error", + "summary": "A request involving an Exchange product failed for a reason that could not be attributed to a more specific cause. A product is the set of channels a publisher exposes for subscribers to receive." + }, + "71201": { + "identifier": "product_not_found", + "title": "Product not found", + "summary": "The Exchange request referred to a product that does not exist. This usually means the product identifier was incorrect or the product is no longer available from the publisher." + }, + "71202": { + "identifier": "product_disabled", + "title": "Product disabled", + "summary": "The Exchange product exists but has been disabled by the publisher, so its channels are not currently available to subscribers." + }, + "71203": { + "identifier": "channel_not_part_of_product", + "title": "Channel not part of product", + "summary": "The requested channel is not one of the channels that the Exchange product exposes, so it cannot be received through that product." + }, + "71204": { + "identifier": "forced_re_attachment_after_product_remapping", + "title": "Forced re-attachment after product remapping", + "summary": "An Exchange channel was detached and re-attached because the product was remapped to a different channel namespace, so the existing attachment no longer pointed at the channel the product now exposes." + }, + "71300": { + "identifier": "exchange_subscription_error", + "title": "Exchange subscription error", + "summary": "A request involving an Exchange subscription failed for a reason that could not be attributed to a more specific cause. A subscription is what grants a subscriber access to a publisher's product." + }, + "71301": { + "identifier": "subscription_disabled", + "title": "Subscription disabled", + "summary": "The subscription to the Exchange product exists but has been disabled, so its channels are not currently available to the subscriber." + }, + "71302": { + "identifier": "no_subscription_to_product", + "title": "No subscription to product", + "summary": "The requester attempted to receive an Exchange product they are not subscribed to. Access to a product's channels requires an active subscription to that product." + }, + "71303": { + "identifier": "channel_outside_subscription_filter", + "title": "Channel outside subscription filter", + "summary": "The requested channel does not match the channel filter defined for the subscription to the Exchange product, so it falls outside the set of channels that subscription grants access to." + }, + "72000": { + "identifier": "livesync_operation_failed", + "title": "LiveSync connector operation failed", + "summary": "A LiveSync database connector hit an unexpected error while running, so the operation it was attempting did not complete." + }, + "72001": { + "identifier": "livesync_publish_failed", + "title": "LiveSync connector could not publish", + "summary": "A LiveSync database connector read a change from the database but could not publish the resulting message to Ably. The change may not have reached the channel it was destined for." + }, + "72002": { + "identifier": "livesync_table_unhealthy", + "title": "LiveSync database connector is unhealthy", + "summary": "A LiveSync database connector could not read from the table or collection it is configured to use, because that table or collection is missing, renamed, or no longer accessible to the connector." + }, + "72003": { + "identifier": "livesync_cannot_connect_db", + "title": "LiveSync connector cannot reach the database", + "summary": "A LiveSync database connector could not establish a connection to the database it reads from. This often points to the database being unreachable, or to incorrect connection details or credentials in the connector configuration." + }, + "72004": { + "identifier": "livesync_no_channel_key", + "title": "LiveSync connector could not identify target channel", + "summary": "A LiveSync database connector read a change but could not determine which channel to publish it to, because the change had no _ablyChannel field. Only changes that specify a channel can be published." + }, + "72005": { + "identifier": "livesync_invalid_pipeline", + "title": "LiveSync MongoDB pipeline is invalid", + "summary": "A LiveSync MongoDB connector could not start because the aggregation pipeline in its configuration was not accepted by MongoDB, for example because it references a stage or field that is not allowed." + }, + "72006": { + "identifier": "livesync_cannot_resume", + "title": "LiveSync connector failed to resume", + "summary": "A LiveSync MongoDB connector could not resume its change stream from where it left off. It stores a resume token to continue after a restart, and this arises when that stored position cannot be read back." + }, + "72007": { + "identifier": "livesync_cannot_store_resume_token", + "title": "LiveSync connector failed to track its position", + "summary": "A LiveSync MongoDB connector could not save its change-stream resume token to the database. The token records how far the connector has read, so that it can continue from the same point after a restart." + }, + "72008": { + "identifier": "livesync_change_stream_history_lost", + "title": "LiveSync connector failed to read history", + "summary": "A LiveSync MongoDB connector's change stream could no longer continue from its last recorded position, because the history it needed was no longer available in the database. Changes made in the intervening period may have been missed." + }, + "80000": { + "identifier": "connection_failed", + "title": "Connection failed", + "summary": "The connection moved to the failed state, a terminal condition from which the SDK does not automatically reconnect. It differs from a temporary disconnection, where the SDK keeps retrying to restore the connection on its own." + }, + "80001": { + "identifier": "connection_failed_no_transport", + "title": "Connection failed as no usable transport available", + "summary": "No usable transport could be established to carry the connection, for example when the network blocks both WebSocket and the HTTP-based fallbacks." + }, + "80002": { + "identifier": "connection_suspended", + "title": "Connection suspended", + "summary": "The connection moved to the suspended state after being disconnected for an extended period. A connection becomes suspended once it has been disconnected for around two minutes, beyond which its state can no longer be recovered." + }, + "80003": { + "identifier": "connection_disconnected", + "title": "Connection disconnected", + "summary": "The connection to Ably was dropped. This is a normal, usually brief interruption, such as a network change or Ably cycling the connection, rather than a deliberate close, and the connection is expected to be re-established." + }, + "80004": { + "identifier": "connection_already_established", + "title": "Connection already established", + "summary": "A request to open a connection was made while a connection was already active." + }, + "80005": { + "identifier": "connection_no_longer_available", + "title": "Connection no longer available", + "summary": "A request referenced an existing connection, but the server no longer held its state — typically because the connection had been idle and was released after a period without activity. The client is signaled to reconnect and continue on a fresh connection." + }, + "80006": { + "identifier": "connection_messages_expired", + "title": "Connection continuity not guaranteed as messages expired", + "summary": "A connection was resumed after the messages needed to bridge the gap had expired. The connection continues, but any messages published during the disconnection may not be redelivered, so continuity across it is not guaranteed." + }, + "80007": { + "identifier": "connection_message_limit_exceeded", + "title": "Connection continuity not guaranteed as message limit exceeded", + "summary": "A connection was resumed, but more messages accumulated during the disconnection than can be held for recovery. The connection continues, but some of those messages may not be redelivered, so continuity across the gap is not guaranteed." + }, + "80008": { + "identifier": "connection_recovery_failed_on_an_outdated_sdk", + "title": "Connection recovery failed on an outdated SDK", + "summary": "An older Ably SDK could not recover a dropped connection. This code is produced only by SDK versions that use a retired version of the Ably protocol; current SDKs connect differently and do not report it." + }, + "80009": { + "identifier": "connection_not_established", + "title": "Connection not established", + "summary": "An operation that requires an open connection was attempted while the client was not connected." + }, + "80010": { + "identifier": "invalid_operation_on_the_connection", + "title": "Invalid operation on the connection", + "summary": "An operation was attempted on a connection that was not in a valid state to carry it, such as one that had already been closed or replaced." + }, + "80011": { + "identifier": "connection_incompatible_auth_params", + "title": "Connection state recovery failed due to incompatible auth", + "summary": "A dropped connection could not be recovered because the authentication details presented on the new connection did not match those of the original, so the previous connection could not be continued." + }, + "80012": { + "identifier": "connection_invalid_serial", + "title": "Connection continuity not guaranteed as serial was invalid", + "summary": "A connection was resumed without a valid connection serial to identify the point it had reached, so there was no reliable position to continue from. The connection continues, but continuity across the gap is not guaranteed." + }, + "80013": { + "identifier": "protocol_error", + "title": "Protocol error", + "summary": "A message exchanged over the connection did not conform to the Ably protocol. The connection received something it could not interpret as a valid protocol message." + }, + "80014": { + "identifier": "connection_timed_out", + "title": "Connection timed out", + "summary": "The connection was not established, or a response was not received, within the time allowed. This often points to a slow or unreliable network path between the client and Ably." + }, + "80015": { + "identifier": "incompatible_connection_parameters", + "title": "Incompatible connection parameters", + "summary": "The connection could not be established because the parameters supplied when opening it were not compatible with one another or with what Ably supports." + }, + "80016": { + "identifier": "connection_replaced_by_a_newer_one", + "title": "Connection replaced by a newer one", + "summary": "An operation was attempted on a connection that was no longer current — a newer connection had replaced it, or its transport handle had been recycled — so the operation could not be applied and the client re-establishes to continue." + }, + "80017": { + "identifier": "connection_closed", + "title": "Connection closed", + "summary": "The connection was closed deliberately, rather than dropped. This is the expected outcome when the connection is closed by request and is not a fault." + }, + "80018": { + "identifier": "invalid_connection_key", + "title": "Invalid connection key", + "summary": "A client tried to re-use a previous connection ID with a connection key, but the key was not in a valid format, so it could not keep that connection ID and was given a new one." + }, + "80019": { + "identifier": "token_request_failed", + "title": "Token request failed", + "summary": "The Ably SDK failed to retrieve a token from the configured authUrl or authCallback." + }, + "80020": { + "identifier": "connection_discontinuity_message_rate_exceeded", + "title": "Continuity lost as message delivery rate exceeded", + "summary": "Messages were dropped because they were being delivered to the connection faster than its per-connection rate limit allows. Continuity is lost on the affected channels: they keep receiving new messages, but the dropped ones are not redelivered." + }, + "80021": { + "identifier": "rate_limit_exceeded_account_connection_creation", + "title": "Account-wide connection creation rate exceeded", + "summary": "A connection was refused because new connections were being opened across the account faster than the permitted rate. The limit applies to the account as a whole rather than to any single connection." + }, + "80022": { + "identifier": "connection_not_found", + "title": "Connection not found", + "summary": "A request referred to a connection that the server could not find, so the exchange could not continue and the client is signaled to reconnect. This is a lookup failure, not a loss of message continuity." + }, + "80023": { + "identifier": "connection_re_established_in_a_different_region", + "title": "Connection re-established in a different region", + "summary": "The client reconnected to a different Ably region from the one it was using, so it could not keep its connection ID and was given a new one." + }, + "80024": { + "identifier": "outdated_ably_sdk_version", + "title": "Outdated Ably SDK version", + "summary": "The connecting Ably SDK is an old version, and the way it connects to Ably is being retired. Connections from these SDK versions increasingly fail until they can no longer connect." + }, + "80030": { + "identifier": "client_restriction_not_satisfied", + "title": "Connection failed due to a client restriction", + "summary": "The connection was refused because a restriction placed on the client was not met. Restrictions can limit which clients are allowed to connect based on conditions set for the application." + }, + "90000": { + "identifier": "channel_operation_failed", + "title": "Channel operation failed", + "summary": "A channel operation, such as attaching, detaching, or publishing, could not be completed." + }, + "90001": { + "identifier": "channel_operation_failed_invalid_channel_state", + "title": "Channel in an invalid state for the operation", + "summary": "A channel operation was attempted while the channel was not in a state that permits it, such as publishing or performing an action on a channel that is not currently attached." + }, + "90002": { + "identifier": "channel_history_could_not_be_retrieved", + "title": "Channel history could not be retrieved", + "summary": "A request for a channel's message history could not be completed, because the position it asked to read from could not be resolved — either the query was incomplete or the point requested is no longer retained." + }, + "90003": { + "identifier": "channel_continuity_not_guaranteed", + "title": "Channel continuity not guaranteed", + "summary": "The client resumed a channel after being disconnected long enough that the point it had reached is no longer available to resume from. The channel reattaches from the earliest point still available, but continuity up to that point is not guaranteed." + }, + "90004": { + "identifier": "channel_backlog_too_large", + "title": "Channel backlog too large", + "summary": "The client requested to resume or rewind a channel whose backlog held more messages than a single replay can deliver. The channel attaches, but the messages beyond that limit are not replayed." + }, + "90005": { + "identifier": "channel_resumed_in_a_different_region", + "title": "Channel resumed in a different region", + "summary": "The client reconnected to a different Ably region from the one it was using, so the position it resumed from could not be applied. The channel attaches at the current point, and no earlier messages are replayed." + }, + "90006": { + "identifier": "channel_history_request_had_no_limit", + "title": "Channel history request had no limit", + "summary": "A request for a channel's message history could not be served because it asked for the complete history with no limit on how many messages to return." + }, + "90007": { + "identifier": "channel_operation_timed_out", + "title": "Channel operation timed out", + "summary": "A channel attach or detach did not complete within the expected time because no response was received from Ably. The operation may not have taken effect, and is usually the result of a transient interruption." + }, + "90008": { + "identifier": "attach_point_not_found", + "title": "Attach point too old for untilAttach history", + "summary": "A history request using untilAttach relies on the channel's attach point still being among recent messages. In this case it was not, so the request could not be served and no messages were returned." + }, + "90009": { + "identifier": "subscribe_mode_not_enabled", + "title": "Subscribe operation failed as subscribe mode not enabled", + "summary": "Messages could not be delivered to a subscriber because the channel had not requested the subscribe mode in its channel options. Messages are only delivered to clients that request that mode, so the listener never fires." + }, + "90010": { + "identifier": "too_many_channels", + "title": "Too many channels", + "summary": "An operation was rejected because it would exceed the maximum number of channels permitted, either the channels attached to a single connection or those included in a single batch request." + }, + "90021": { + "identifier": "rate_limit_exceeded_account_channel_creation", + "title": "Account-wide channel creation rate exceeded", + "summary": "Requests were rejected because the rate at which new channels were being created across the account exceeded the permitted limit. The limit counts channels newly activated within a period, not those already active." + }, + "91000": { + "identifier": "cannot_enter_presence_without_a_client_id", + "title": "Cannot enter presence without a clientId", + "summary": "A request to enter the presence set was rejected because the connection had no clientId. Presence members are identified by their clientId, so one is required to enter." + }, + "91001": { + "identifier": "cannot_enter_presence_in_the_channels_current_state", + "title": "Cannot enter presence in the channel's current state", + "summary": "A request to enter the presence set was rejected because the channel was not in a state that permits presence operations, such as a channel that is detached, suspended, or failed." + }, + "91002": { + "identifier": "cannot_leave_presence_that_was_never_entered", + "title": "Cannot leave presence that was never entered", + "summary": "A request to leave the presence set was rejected because the clientId was not a member of it." + }, + "91003": { + "identifier": "too_many_presence_members", + "title": "Too many presence members", + "summary": "A request to enter the presence set was rejected because the channel already held the maximum number of presence members permitted. The limit counts the members present on a single channel." + }, + "91004": { + "identifier": "unable_to_automatically_re_enter_presence", + "title": "Unable to automatically re-enter presence", + "summary": "After reconnecting, the Ably SDK tried to re-enter the presence set on the client's behalf but the attempt did not succeed, so the member was not restored to the presence set." + }, + "91005": { + "identifier": "presence_state_out_of_sync", + "title": "Presence state out of sync", + "summary": "The presence set held by the client no longer matched the state on Ably. The presence members are re-synchronized so the two are brought back into agreement." + }, + "91006": { + "identifier": "total_presence_set_data_too_large", + "title": "Total presence set data too large", + "summary": "A request to enter the presence set was rejected because the combined size of the data held by members on the channel would exceed the maximum permitted for the presence set." + }, + "91007": { + "identifier": "unexpected_loss_of_presence_membership", + "title": "Unexpected loss of presence membership", + "summary": "The connection was removed from the presence set despite still being active, so the channel was detached to prompt the Ably SDK to automatically re-enter presence. The condition is usually transient." + }, + "91008": { + "identifier": "presence_subscribe_mode_not_enabled", + "title": "Presence operation failed as presence_subscribe mode not enabled", + "summary": "The presence members of a channel could not be retrieved because the channel had not requested the presence_subscribe mode in its channel options. Presence state is only delivered to clients that request that mode." + }, + "91100": { + "identifier": "presence_member_left_as_their_connection_closed", + "title": "Presence member left as their connection closed", + "summary": "A presence member was removed from the presence set because it was no longer present, usually because the connection it entered on was closed." + }, + "92000": { + "identifier": "invalid_object_message", + "title": "Invalid LiveObjects message", + "summary": "A LiveObjects message was rejected because it was invalid or did not conform to the expected structure. This usually indicates a problem with how the object operation was constructed before it was sent." + }, + "92001": { + "identifier": "object_limit_exceeded", + "title": "LiveObjects limit exceeded", + "summary": "A LiveObjects operation was rejected because it would take the channel beyond the maximum number of objects it is allowed to hold. The limit is set by the account." + }, + "92002": { + "identifier": "operation_on_tombstone_object", + "title": "LiveObjects operation on a deleted object", + "summary": "An operation could not be applied because the target LiveObject had already been deleted. A deleted object is retained only as a marker, or tombstone, and can no longer be modified." + }, + "92003": { + "identifier": "object_root_deleted", + "title": "LiveObjects root has been deleted", + "summary": "A LiveObjects object tree could not be fetched because the object at its root had already been deleted. A deleted object is retained only as a marker, or tombstone, and cannot serve as the root of a tree." + }, + "92004": { + "identifier": "object_not_found", + "title": "LiveObjects object not found", + "summary": "A LiveObjects operation referenced an object that does not exist on the channel. The object may never have been created, or it may have been removed before the operation was applied." + }, + "92005": { + "identifier": "no_objects_at_path", + "title": "LiveObjects path matched no objects", + "summary": "A LiveObjects operation specified a path within an object tree that did not resolve to any object. The path may be incorrect, or the objects it points to may not exist." + }, + "92006": { + "identifier": "object_operation_missing_identifier_or_path", + "title": "LiveObjects operation missing object reference", + "summary": "A LiveObjects operation could not be performed because it specified neither an object identifier nor a path." + }, + "92007": { + "identifier": "object_operation_path_not_processable", + "title": "LiveObjects operation not valid for the path", + "summary": "A LiveObjects operation could not be applied to the object at the specified path, because the operation is not compatible with the kind of object found there." + }, + "92008": { + "identifier": "objects_sync_did_not_complete", + "title": "LiveObjects sync did not complete", + "summary": "A LiveObjects operation could not be applied because the channel had not finished synchronizing its objects." + }, + "93001": { + "identifier": "annotation_subscribe_mode_not_enabled", + "title": "Annotation listener without annotation_subscribe mode", + "summary": "An annotation listener was added to a channel that had not requested the annotation_subscribe mode in its channel options. Annotations are only delivered to clients that explicitly request them, so the listener will not receive anything." + }, + "93002": { + "identifier": "mutable_messages_not_enabled", + "title": "Message annotations, updates, appends, and deletes not enabled", + "summary": "An operation could not be performed because it requires a feature that is enabled by the channel namespace's 'Message annotations, updates, appends, and deletes' setting." + }, + "101000": { + "identifier": "space_name_is_empty", + "title": "Space name is empty", + "summary": "A space could not be created or referenced because no name was supplied." + }, + "101001": { + "identifier": "space_must_be_entered_first", + "title": "Space must be entered first", + "summary": "An operation was attempted that requires having first entered the space. In Ably Spaces, actions such as updating a member's location or profile are only available once the space has been entered." + }, + "101002": { + "identifier": "lock_request_already_pending", + "title": "Lock request already pending", + "summary": "A lock could not be requested because a request for the same lock is already in progress. In Ably Spaces, a member may only have one outstanding request for a given lock at a time." + }, + "101003": { + "identifier": "lock_already_held", + "title": "Lock already held", + "summary": "A lock could not be acquired because it is currently held by another member. In Ably Spaces, only one member can hold a given lock at a time, and it must be released before another can take it." + }, + "101004": { + "identifier": "lock_invalidated_by_concurrent_request", + "title": "Lock invalidated by concurrent request", + "summary": "A lock that appeared to be acquired was invalidated because another member requested the same lock concurrently and now holds it." + }, + "102000": { + "identifier": "invalid_chat_room_name", + "title": "Invalid chat room name", + "summary": "A Chat room could not be used because the name supplied for it was not valid." + }, + "102001": { + "identifier": "room_attach_failed_for_messages", + "title": "Room attach failed for messages", + "summary": "Attaching a Chat room failed because its messages feature could not be attached." + }, + "102002": { + "identifier": "room_attach_failed_for_presence", + "title": "Room attach failed for presence", + "summary": "Attaching a Chat room failed because its presence feature could not be attached." + }, + "102003": { + "identifier": "room_attach_failed_for_reactions", + "title": "Room attach failed for reactions", + "summary": "Attaching a Chat room failed because its reactions feature could not be attached." + }, + "102004": { + "identifier": "room_attach_failed_for_occupancy", + "title": "Room attach failed for occupancy", + "summary": "Attaching a Chat room failed because its occupancy feature could not be attached." + }, + "102005": { + "identifier": "room_attach_failed_for_typing", + "title": "Room attach failed for typing", + "summary": "Attaching a Chat room failed because its typing feature could not be attached." + }, + "102050": { + "identifier": "room_detach_failed_for_messages", + "title": "Room detach failed for messages", + "summary": "Detaching a Chat room failed because its messages feature could not be detached." + }, + "102051": { + "identifier": "room_detach_failed_for_presence", + "title": "Room detach failed for presence", + "summary": "Detaching a Chat room failed because its presence feature could not be detached." + }, + "102052": { + "identifier": "room_detach_failed_for_reactions", + "title": "Room detach failed for reactions", + "summary": "Detaching a Chat room failed because its reactions feature could not be detached." + }, + "102053": { + "identifier": "room_detach_failed_for_occupancy", + "title": "Room detach failed for occupancy", + "summary": "Detaching a Chat room failed because its occupancy feature could not be detached." + }, + "102054": { + "identifier": "room_detach_failed_for_typing", + "title": "Room detach failed for typing", + "summary": "Detaching a Chat room failed because its typing feature could not be detached." + }, + "102100": { + "identifier": "room_continuity_not_guaranteed", + "title": "Room continuity not guaranteed", + "summary": "A discontinuity was detected on the Chat room, so the continuity of messages could not be guaranteed. This usually follows a disconnection long enough that the room could not resume from where it left off." + }, + "102101": { + "identifier": "room_is_in_the_failed_state", + "title": "Room is in the failed state", + "summary": "The operation could not be performed because the Chat room is in the failed state. A room enters this state when an earlier operation on it failed and could not recover." + }, + "102102": { + "identifier": "room_is_being_released", + "title": "Room is being released", + "summary": "The operation could not be performed because the Chat room is in the process of being released." + }, + "102103": { + "identifier": "room_has_been_released", + "title": "Room has been released", + "summary": "The operation could not be performed because the Chat room has already been released." + }, + "102104": { + "identifier": "room_operation_failed_after_earlier_attempt", + "title": "Room operation failed after earlier attempt", + "summary": "The operation failed because a preceding attempt to perform it had already failed." + }, + "102105": { + "identifier": "unknown_room_lifecycle_error", + "title": "Unknown room lifecycle error", + "summary": "An unexpected error occurred in the Chat room lifecycle that does not correspond to a known condition." + }, + "102106": { + "identifier": "room_released_before_operation_completed", + "title": "Room released before operation completed", + "summary": "The operation could not complete because the Chat room was released while it was still in progress." + }, + "102107": { + "identifier": "room_already_exists_with_different_options", + "title": "Room already exists with different options", + "summary": "A Chat room with the same name already exists but was created with different options. A room must be released before it can be requested again with a different configuration." + }, + "102108": { + "identifier": "feature_not_enabled_in_room_options", + "title": "Feature not enabled in room options", + "summary": "The operation required a feature that was not enabled when the Chat room was created. Features such as presence or typing indicators must be enabled in the room options to be used." + }, + "102109": { + "identifier": "history_requested_for_an_unsubscribed_listener", + "title": "History requested for an unsubscribed listener", + "summary": "A request for the messages published before a listener's subscription point was rejected because that listener was not subscribed. This history is only available while the listener is subscribed to the room." + }, + "102110": { + "identifier": "channel_serial_not_defined", + "title": "Channel serial not defined", + "summary": "A channel serial was expected but was not defined at the point it was needed." + }, + "102111": { + "identifier": "channel_options_modified_after_request", + "title": "Channel options modified after request", + "summary": "Channel options could not be changed because the underlying channel had already been requested." + }, + "102112": { + "identifier": "room_is_in_an_invalid_state", + "title": "Room is in an invalid state", + "summary": "The operation could not be performed because the Chat room was in a state that does not permit it." + }, + "102113": { + "identifier": "sequential_execution_could_not_be_enforced", + "title": "Sequential execution could not be enforced", + "summary": "The operation failed because its sequential execution could not be enforced. Chat room lifecycle operations are expected to run one at a time, and that ordering could not be guaranteed." + }, + "102200": { + "identifier": "hook_used_outside_its_provider", + "title": "Hook used outside its provider", + "summary": "An Ably Chat React hook was used in a component that is not wrapped in the required provider. The hooks depend on context supplied by a provider higher in the component tree." + }, + "102201": { + "identifier": "component_unmounted_before_completion", + "title": "Component unmounted before completion", + "summary": "An operation started by the Ably Chat React hooks could not finish because the React component was unmounted while it was still in progress." + }, + "102202": { + "identifier": "presence_data_could_not_be_fetched", + "title": "Presence data could not be fetched", + "summary": "The Ably Chat React hooks could not retrieve presence data after the maximum number of retries. This usually points to a persistent problem reaching presence rather than a transient one." + }, + "103000": { + "identifier": "unable_to_publish_push_notification", + "title": "Push notification internal error", + "summary": "The push notification was not delivered because of an unexpected error within the Ably platform during preparation, dispatch, or requeue. The cause is not related to the contents of the request or the device's registration." + }, + "103001": { + "identifier": "push_publish_retries_exhausted", + "title": "Push notification retry limit reached", + "summary": "The push notification was not delivered because the configured retry limit was reached after repeated unsuccessful delivery attempts. The underlying failures are typically transient — provider outages, rate limits, or network errors." + }, + "103002": { + "identifier": "push_direct_publish_no_recipient", + "title": "Push notification recipient missing", + "summary": "The push notification was not delivered because the direct push request was submitted without a recipient device. Direct push targets a single device by ID and cannot be processed when no recipient is supplied." + }, + "103003": { + "identifier": "push_cannot_handle_body", + "title": "Push notification body invalid", + "summary": "The push notification was not delivered because its body could not be processed. This typically indicates a malformed or unsupported message structure in the publish request." + }, + "103004": { + "identifier": "push_notification_rejected_by_provider", + "title": "Push notification rejected by provider", + "summary": "The push notification was not delivered because the provider — APNs, FCM, or WebPush — rejected the request due to a problem with the payload or delivery parameters. Typical causes include an oversized payload, a disallowed topic, or an unsupported field." + }, + "103005": { + "identifier": "push_device_token_invalid", + "title": "Push device token invalid", + "summary": "The push notification was not delivered because the provider — APNs, FCM, or WebPush — rejected the device's push token as invalid for the configured application. The token may have been issued under different credentials or for a different application." + }, + "103006": { + "identifier": "push_transport_not_configured", + "title": "Push transport not configured", + "summary": "The push notification was not delivered because the device's push notification provider — APNs, FCM, or WebPush — has no credentials configured on the application. No deliveries can be made through that provider until the credentials are present." + }, + "103007": { + "identifier": "push_notification_provider_unreachable", + "title": "Push notification provider unreachable", + "summary": "The push notification was not delivered because the provider — APNs, FCM, or WebPush — could not be reached, returned a server error, or sent a response that could not be processed. The condition is usually transient." + }, + "103008": { + "identifier": "push_transport_credentials_invalid", + "title": "Push transport credentials invalid", + "summary": "A push notification could not be sent because the credentials configured for the target platform were rejected or had expired, such as an expired APNs certificate." + } + } } From fcc793b05663d59665377dcdf5276c3a4a08ba89 Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Fri, 3 Jul 2026 12:40:36 +0100 Subject: [PATCH 8/9] Remove the Ranges section from protocol/README.md The hand-maintained ranges table had drifted from reality and duplicated information now carried per-code in the error registry. Drop it rather than keep a second, stale source. Co-Authored-By: Claude Opus 4.8 (1M context) --- protocol/README.md | 54 ---------------------------------------------- 1 file changed, 54 deletions(-) diff --git a/protocol/README.md b/protocol/README.md index f036e291..db12bee6 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -10,60 +10,6 @@ See [the `ably/help` repository (internal)](https://github.com/ably/help), the " If you need help understanding any error codes, or need technical support, please visit the [Ably support desk](https://www.ably.io/support). -### Ranges - -Codes in [errors.json](errors.json) fall into these ranges: - -| From | Title | -| ----- | ----- | -| 10000 | Generic | -| 40000 | 400 | -| 40100 | 401 | -| 40300 | 403 | -| 40400 | 404 | -| 40500 | 405 | -| 40900 | 409 | -| 41001 | 410 | -| 42200 | 422 | -| 42910 | 429 | -| 50000 | 500 | -| 50010 | Edge cacheing / proxy service | -| 50320 | Active Traffic Management error code to indicate intentional redirect of traffic to fallback hosts | -| 50330 | DNS switch over | -| 50410 | Edge cacheing / proxy service | -| 60000 | reserved for internal (non-customer-facing) use | -| 70000 | reactor-related | -| 71000 | exchange-related: general | -| 71100 | exchange-related: publisher | -| 71200 | exchange-related: product | -| 71300 | exchange-related: subscription | -| 80000 | connection-related | -| 90000 | channel-related | - -Codes above `100000` are reserved for use by specific Ably projects and ecosystems, where these codes are unknown to the core Ably service. Definitions for these ranges are defined below. - -#### Asset Tracking - -Codes for Asset Tracking are maintained externally. - -| From | To | Location of Definitions | -| ---- | -- | ----------------------- | -| 100000 | 100999 | [Specification: Error codes](https://github.com/ably/ably-asset-tracking-common/tree/main/specification#error-codes) | - -#### Spaces - -| From | Title | -| ----- | ----- | -| 101000 | Space name missing | -| 101001 | Not entered space | -| 101002 | Lock request exists | -| 101003 | Lock is locked | -| 101004 | Lock invalidated | - -#### Chat - -102000 - 102999 are reserved for chat purposes. - ## Agents A list of agents announced by Ably client libraries can be found in [agents.json](agents.json). From 1abd6817059ac14e32a3e1e8dcf44044c9f17b7c Mon Sep 17 00:00:00 2001 From: Lewis Marshall Date: Tue, 7 Jul 2026 21:35:34 +0100 Subject: [PATCH 9/9] Address PR review feedback on error entries - 93002: rename identifier to message_updates_not_enabled (avoid exposing "mutable messages") and reword the summary to reference the channel rule. - 80016: rewrite the summary as shorter sentences. - 90008, 40024: code-style `untilAttach` in the body text. Regenerate protocol/errors.json to match. Co-Authored-By: Claude Opus 4.8 (1M context) --- errors/codes/40024.md | 6 +++--- errors/codes/80016.md | 2 +- errors/codes/90008.md | 8 ++++---- errors/codes/93002.md | 4 ++-- protocol/errors.json | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/errors/codes/40024.md b/errors/codes/40024.md index 3fcf15b7..28edf1e7 100644 --- a/errors/codes/40024.md +++ b/errors/codes/40024.md @@ -9,8 +9,8 @@ summary: A history request using untilAttach was handled by a different Ably reg Retrying may not help, as the retry might again be handled by a different region. There is no automatic recovery, so choose the approach that best fits your application: -- **Retry without untilAttach and remove duplicates yourself.** Request [history](https://ably.com/docs/storage-history/history) without untilAttach and discard any messages you have already received in real time. This keeps a gap-free record but your application has to deduplicate. -- **Retry without untilAttach and accept possible duplicates.** Less work, but some messages may appear both in the history response and among those already received in real time. +- **Retry without `untilAttach` and remove duplicates yourself.** Request [history](https://ably.com/docs/storage-history/history) without `untilAttach` and discard any messages you have already received in real time. This keeps a gap-free record but your application has to deduplicate. +- **Retry without `untilAttach` and accept possible duplicates.** Less work, but some messages may appear both in the history response and among those already received in real time. - **Treat it as having no retrievable history.** If missing the earlier messages is acceptable, continue with only the messages received in real time. Which is best depends on whether duplicate messages or missing history is worse for your use case. @@ -19,7 +19,7 @@ Which is best depends on whether duplicate messages or missing history is worse Each Ably region observes and orders recent messages independently, so a given point in one region's order does not necessarily correspond to the same point in another's. -A history request using untilAttach returns the messages published up to the point at which the channel attached — a point in the order of the region it attached in. If the request is handled by a different region, that region cannot reliably determine which messages fall before that point, so rather than risk returning the wrong set it does not serve the request. +A history request using `untilAttach` returns the messages published up to the point at which the channel attached — a point in the order of the region it attached in. If the request is handled by a different region, that region cannot reliably determine which messages fall before that point, so rather than risk returning the wrong set it does not serve the request. ## What you'll see diff --git a/errors/codes/80016.md b/errors/codes/80016.md index 62270e30..dac8c1d4 100644 --- a/errors/codes/80016.md +++ b/errors/codes/80016.md @@ -2,5 +2,5 @@ code: 80016 identifier: connection_replaced_by_a_newer_one title: Connection replaced by a newer one -summary: An operation was attempted on a connection that was no longer current — a newer connection had replaced it, or its transport handle had been recycled — so the operation could not be applied and the client re-establishes to continue. +summary: An operation was attempted on a connection that is no longer current, so it could not be applied. A newer connection had replaced it, or its transport handle had been recycled. The client re-establishes the connection to continue. --- diff --git a/errors/codes/90008.md b/errors/codes/90008.md index 2c73eae3..298ab9a2 100644 --- a/errors/codes/90008.md +++ b/errors/codes/90008.md @@ -9,9 +9,9 @@ summary: A history request using untilAttach relies on the channel's attach poin Whether this matters depends on whether the channel has [persistence](https://ably.com/docs/storage-history/storage) enabled. -Without persistence, there is nothing to do and the error can be ignored. The client already has all recent messages, because it has received everything since its attach point and that point is older than the recent window. The older history [untilAttach](https://ably.com/docs/storage-history/history) would have returned is not retained, so there is nothing more to recover. +Without persistence, there is nothing to do and the error can be ignored. The client already has all recent messages, because it has received everything since its attach point and that point is older than the recent window. The older history [`untilAttach`](https://ably.com/docs/storage-history/history) would have returned is not retained, so there is nothing more to recover. -With persistence, the older history untilAttach would have returned is retained, and can be fetched with a plain [history](https://ably.com/docs/storage-history/history) request without untilAttach. That response won't line up exactly with the messages the client already received from the channel, so choose how to reconcile them: +With persistence, the older history `untilAttach` would have returned is retained, and can be fetched with a plain [history](https://ably.com/docs/storage-history/history) request without `untilAttach`. That response won't line up exactly with the messages the client already received from the channel, so choose how to reconcile them: - **Remove duplicates yourself.** Discard any messages in the response that the client already received. This keeps a gap-free record but your application has to deduplicate. - **Accept possible duplicates.** Less work, but some messages may appear both in the response and among those already received. @@ -21,9 +21,9 @@ Which is best depends on whether duplicate messages or missing history is worse ## Why it happens -untilAttach returns history up to the attach point, and it works from Ably's window of recent messages — about two minutes. It can therefore only serve an attach point that still falls within that window. +`untilAttach` returns history up to the attach point, and it works from Ably's window of recent messages — about two minutes. It can therefore only serve an attach point that still falls within that window. -This error means the attach point was older than that, so untilAttach could not use it. The messages themselves are unaffected; whether the older ones can still be retrieved depends on the channel's persistence. +This error means the attach point was older than that, so `untilAttach` could not use it. The messages themselves are unaffected; whether the older ones can still be retrieved depends on the channel's persistence. ## What you'll see diff --git a/errors/codes/93002.md b/errors/codes/93002.md index 1158c6ce..7772f4fe 100644 --- a/errors/codes/93002.md +++ b/errors/codes/93002.md @@ -1,6 +1,6 @@ --- code: 93002 -identifier: mutable_messages_not_enabled +identifier: message_updates_not_enabled title: Message annotations, updates, appends, and deletes not enabled -summary: An operation could not be performed because it requires a feature that is enabled by the channel namespace's 'Message annotations, updates, appends, and deletes' setting. +summary: An operation could not be performed because the channel did not have the 'Message annotations, updates, appends, and deletes' rule enabled. --- diff --git a/protocol/errors.json b/protocol/errors.json index 48d0976f..ae9db147 100644 --- a/protocol/errors.json +++ b/protocol/errors.json @@ -850,7 +850,7 @@ "80016": { "identifier": "connection_replaced_by_a_newer_one", "title": "Connection replaced by a newer one", - "summary": "An operation was attempted on a connection that was no longer current — a newer connection had replaced it, or its transport handle had been recycled — so the operation could not be applied and the client re-establishes to continue." + "summary": "An operation was attempted on a connection that is no longer current, so it could not be applied. A newer connection had replaced it, or its transport handle had been recycled. The client re-establishes the connection to continue." }, "80017": { "identifier": "connection_closed", @@ -1058,9 +1058,9 @@ "summary": "An annotation listener was added to a channel that had not requested the annotation_subscribe mode in its channel options. Annotations are only delivered to clients that explicitly request them, so the listener will not receive anything." }, "93002": { - "identifier": "mutable_messages_not_enabled", + "identifier": "message_updates_not_enabled", "title": "Message annotations, updates, appends, and deletes not enabled", - "summary": "An operation could not be performed because it requires a feature that is enabled by the channel namespace's 'Message annotations, updates, appends, and deletes' setting." + "summary": "An operation could not be performed because the channel did not have the 'Message annotations, updates, appends, and deletes' rule enabled." }, "101000": { "identifier": "space_name_is_empty",