From b5f9d332f8ce4b5f73158abbfc8195e117732eda Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 19:57:00 +0000 Subject: [PATCH 1/2] fix(service-storage,spec): the upload size refusal answers 413 PAYLOAD_TOO_LARGE The ledger lists PAYLOAD_TOO_LARGE under @objectstack/service-storage beside @objectstack/rest, and the four upload doors stamp it in place of the status-derived VALIDATION_ERROR. Same status, message, doors and refusal; tests, README, docs and the pending storage note follow. Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude --- .changeset/22283-storage-limits-honoured.md | 2 +- ...14-storage-payload-too-large-provenance.md | 11 +++++ content/docs/api/error-catalog.mdx | 43 +++++++++++++------ .../docs/protocol/kernel/error-handling.mdx | 14 +++--- .../storage-upload-limits.dogfood.test.ts | 6 +-- packages/services/service-storage/README.md | 2 +- .../src/error-envelope.conformance.test.ts | 8 ++-- .../src/storage-limits.test.ts | 6 +-- .../service-storage/src/storage-routes.ts | 22 ++++++---- .../spec/src/api/error-code-ledger.zod.ts | 10 +++++ 10 files changed, 84 insertions(+), 40 deletions(-) create mode 100644 .changeset/22314-storage-payload-too-large-provenance.md diff --git a/.changeset/22283-storage-limits-honoured.md b/.changeset/22283-storage-limits-honoured.md index 5fa63f06bcf..8f8858be403 100644 --- a/.changeset/22283-storage-limits-honoured.md +++ b/.changeset/22283-storage-limits-honoured.md @@ -12,7 +12,7 @@ Clause-②: no (narrowing) Setup's File Storage page has always rendered a "Limits" group — "Max upload size (MB)", "Presigned URL TTL (seconds)" and "Upload session TTL (seconds)" — and saved it, but nothing read it. `StorageServicePlugin` now reads the group on every settings pass and its upload doors honour it, on the plugin's own mount and on a host's `mountStorageRoutes` mount alike. -**What stops being accepted.** Where the plugin is bound to the `storage` settings namespace (its default, `bindToSettings: true`, with a settings service present), an upload larger than the resolved `max_upload_mb` is refused with `413` and the standard code `VALIDATION_ERROR`, in the usual error envelope, with a message naming the limit and the setting. Nothing is stored: no `sys_file` or `sys_upload_session` row is written and no byte reaches the adapter. The doors that judge it: +**What stops being accepted.** Where the plugin is bound to the `storage` settings namespace (its default, `bindToSettings: true`, with a settings service present), an upload larger than the resolved `max_upload_mb` is refused with `413` and the registered code `PAYLOAD_TOO_LARGE`, in the usual error envelope, with a message naming the limit and the setting. Nothing is stored: no `sys_file` or `sys_upload_session` row is written and no byte reaches the adapter. The doors that judge it: - `POST /storage/upload/presigned` — the declared `size`; - `POST /storage/upload/chunked` — the declared `totalSize`; diff --git a/.changeset/22314-storage-payload-too-large-provenance.md b/.changeset/22314-storage-payload-too-large-provenance.md new file mode 100644 index 00000000000..8cf44d76b65 --- /dev/null +++ b/.changeset/22314-storage-payload-too-large-provenance.md @@ -0,0 +1,11 @@ +--- +'@objectstack/spec': minor +--- + +feat(spec): the error-code ledger lists `PAYLOAD_TOO_LARGE` under `@objectstack/service-storage` + +Clause-②: yes (widening: a new owner provenance row in the published error-code ledger) + +`ERROR_CODE_LEDGER['@objectstack/service-storage']` now lists `PAYLOAD_TOO_LARGE` (`413`), the code `@objectstack/rest` already registers for its import row ceiling. The storage upload doors answer it for an upload over the File Storage settings' `max_upload_mb`: the presigned upload's declared `size`, the chunked upload's declared `totalSize`, the local raw PUT body and each chunk's running total. + +No code is added. `ErrorCode`, `RegisteredErrorCode` and `REGISTERED_ERROR_CODES` are unchanged, so `ApiErrorSchema` accepts exactly what it accepted before. What widens is the per-package list a reader consults to learn which packages answer a code: a code emitted by several packages is listed under each of them. diff --git a/content/docs/api/error-catalog.mdx b/content/docs/api/error-catalog.mdx index 27fe1ab699b..129ff7551f8 100644 --- a/content/docs/api/error-catalog.mdx +++ b/content/docs/api/error-catalog.mdx @@ -4,7 +4,7 @@ navTitle: Error Code Catalog description: Complete reference for all ObjectStack error codes with causes, fixes, and retry strategies --- -ObjectStack uses a structured error system with **9 error categories** and **51 error codes reachable on the wire**. Every error includes a machine-readable code, HTTP status mapping, and retry guidance. +ObjectStack uses a structured error system with **9 error categories** and **52 error codes reachable on the wire**. Every error includes a machine-readable code, HTTP status mapping, and retry guidance. This catalog documents the **wire face** — the codes a client can actually receive. That is not quite the `StandardErrorCode` enum: the enum also carries in-process spellings the REST door translates at the @@ -62,16 +62,10 @@ entry's cross-reference sentence so the in-process one stays findable. **Fix:** Check the `fields` array for specific field-level issues. **Retry:** `no_retry` - -**`VALIDATION_ERROR` is `400` with one exception: the storage upload doors answer `413` for -an upload over the File Storage settings' `max_upload_mb`.** - -The presigned-upload door (the declared `size`), the chunked-upload start (the declared -`totalSize`), the local adapter's raw upload (the body) and the chunk door (the bytes the -upload already holds plus the chunk) refuse it before anything is stored. The message names -the size, the limit and the setting. Raise the setting or send a smaller file; resending the -same upload cannot succeed. The protocol page carries the same exception: -[`VALIDATION_ERROR`](/docs/protocol/kernel/error-handling#validation_error). + +**A request too large for its door is not this code.** An upload over the File Storage +settings' `max_upload_mb`, and an import over its route's row ceiling, answer `413` +[`PAYLOAD_TOO_LARGE`](#payload_too_large). ```json @@ -510,6 +504,31 @@ an environment scope (no `X-Environment-Id` header and no hostname mapping). --- +## Size Limit Errors (413) + +`PAYLOAD_TOO_LARGE` is an [error-code ledger](/docs/references/api/error-code-ledger) code, listed +under each package that answers it — `@objectstack/rest` and `@objectstack/service-storage` — not a +standard-catalog member, so its status is published here rather than inherited from a category. +The standard catalog names no `413` member. + +### `PAYLOAD_TOO_LARGE` + +**HTTP Status:** 413 +**Cause:** The request carries more than its door accepts: + +- **An upload over the File Storage settings' `max_upload_mb`.** The presigned-upload door (the + declared `size`), the chunked-upload start (the declared `totalSize`), the local adapter's raw + upload (the body) and the chunk door (the bytes the upload already holds plus the chunk) refuse + it before anything is stored. The message names the size, the limit and the setting. +- **An import over its route's row ceiling** — 5,000 rows on the synchronous route, 50,000 on the + job route ([size limits](/docs/data-modeling/import-mappings#size-limits)). + +**Fix:** Raise the setting or send a smaller file; split the import across requests. Resending +the same request cannot succeed. +**Retry:** `no_retry` + +--- + ## Rate Limit Errors (429) ### `RATE_LIMIT_EXCEEDED` @@ -917,7 +936,7 @@ async function handleApiCall() { | 403 | `authorization` | `PERMISSION_DENIED`, `FIELD_NOT_ACCESSIBLE`, `LICENSE_REQUIRED` | | 404 | `not_found` | `RECORD_NOT_FOUND`, `OBJECT_NOT_FOUND`, `ENDPOINT_NOT_FOUND` | | 409 | `conflict` | `CONCURRENT_MODIFICATION`, `UNIQUE_VIOLATION`, `DELETE_RESTRICTED` | -| 413 | `validation` | `VALIDATION_ERROR` on an upload over the storage settings' `max_upload_mb` (see [above](#validation_error)) — this row is an exception to the 400 row, not a second home for the code | +| 413 | `validation` | `PAYLOAD_TOO_LARGE` — an upload over the storage settings' `max_upload_mb`, or an import over its row ceiling (see [above](#payload_too_large)) | | 422 | `validation` | `MISSING_REQUIRED_FIELD` on an absent `controlled_by_parent` master reference (see [above](#missing_required_field)) — this row is an exception to the 400 row, not a second home for the code | | 429 | `rate_limit` | `RATE_LIMIT_EXCEEDED`, `QUOTA_EXCEEDED` | | 500 | `server` | `INTERNAL_ERROR`, `DATABASE_ERROR` | diff --git a/content/docs/protocol/kernel/error-handling.mdx b/content/docs/protocol/kernel/error-handling.mdx index 10927c003bd..47d74ad38b0 100644 --- a/content/docs/protocol/kernel/error-handling.mdx +++ b/content/docs/protocol/kernel/error-handling.mdx @@ -214,7 +214,7 @@ ObjectStack uses standard HTTP status codes: | **403** | Forbidden | Authenticated but insufficient permissions | | **404** | Not Found | Resource doesn't exist | | **409** | Conflict | Resource already exists or version mismatch | -| **413** | Content Too Large | An upload over the storage settings' `max_upload_mb` — answered as [`VALIDATION_ERROR`](#validation_error), its one exception to 400 | +| **413** | Content Too Large | An upload over the storage settings' `max_upload_mb`, or an import over its row ceiling — answered as [`PAYLOAD_TOO_LARGE`](/docs/api/error-catalog#payload_too_large) | | **422** | Unprocessable Entity | Semantic validation failed (e.g. metadata spec validation); also an absent `controlled_by_parent` master reference — see [`MISSING_REQUIRED_FIELD`](#missing_required_field) | | **429** | Too Many Requests | Rate limit exceeded | | **500** | Internal Server Error | Server-side error | @@ -402,7 +402,7 @@ if (body.code === 'VALIDATION_FAILED') { ``` #### `VALIDATION_ERROR` -**HTTP Status:** 400 — with one documented exception, which answers **413** (see below) +**HTTP Status:** 400 **Meaning:** The request is malformed — the generic "you sent something wrong" code **Envelope:** **both**, and one emitter writes neither (see the warning below) @@ -418,12 +418,10 @@ Where it comes from: specific. - **Query-parameter refusals.** `refuseUnknownQueryParams` and `refuseRepeatedQueryParams` answer 400 `VALIDATION_ERROR` for an unrecognised or repeated query parameter. -- **Upload size refusals — the one 413.** The storage upload doors answer **413** - `VALIDATION_ERROR` for an upload over the File Storage settings' `max_upload_mb`: the - presigned-upload door (the declared `size`), the chunked-upload start (the declared - `totalSize`), the local adapter's raw upload (the body) and the chunk door (the bytes the - upload already holds plus the chunk), each before anything is stored. The message names the - size, the limit and the setting. + +Not from a request too large for its door: an upload over the File Storage settings' +`max_upload_mb` answers **413** with the registered code +[`PAYLOAD_TOO_LARGE`](/docs/api/error-catalog#payload_too_large), not this one. **Nested** (the dispatcher door, status-derived — `GET /api/v1/packages/:id?version=1&version=2`): ```json diff --git a/packages/qa/dogfood/test/storage-upload-limits.dogfood.test.ts b/packages/qa/dogfood/test/storage-upload-limits.dogfood.test.ts index 2f6047526f8..53de189b0d4 100644 --- a/packages/qa/dogfood/test/storage-upload-limits.dogfood.test.ts +++ b/packages/qa/dogfood/test/storage-upload-limits.dogfood.test.ts @@ -65,7 +65,7 @@ describe('[#22283] the storage Limits settings are enforced at the upload doors expect(over.status, 'a declared size one byte over 100 MB').toBe(413); const refusal = (await over.json()) as any; expect(refusal.success).toBe(false); - expect(refusal.error.code).toBe('VALIDATION_ERROR'); + expect(refusal.error.code).toBe('PAYLOAD_TOO_LARGE'); const ok = await presign(100 * MIB); expect(ok.status, 'exactly 100 MB').toBe(200); @@ -88,7 +88,7 @@ describe('[#22283] the storage Limits settings are enforced at the upload doors over = await presign(MIB + 1); } expect(over.status, 'a declared size one byte over the saved 1 MB').toBe(413); - expect(((await over.json()) as any).error.code).toBe('VALIDATION_ERROR'); + expect(((await over.json()) as any).error.code).toBe('PAYLOAD_TOO_LARGE'); const ok = await presign(10); expect(ok.status).toBe(200); @@ -99,7 +99,7 @@ describe('[#22283] the storage Limits settings are enforced at the upload doors const path = String(data.uploadUrl).replace(/^https?:\/\/[^/]+/, ''); const tooBig = await rawPut(path, MIB + 1); expect(tooBig.status, 'a body one byte over the saved 1 MB').toBe(413); - expect(((await tooBig.json()) as any).error.code).toBe('VALIDATION_ERROR'); + expect(((await tooBig.json()) as any).error.code).toBe('PAYLOAD_TOO_LARGE'); const fits = await rawPut(path, MIB); expect(fits.status, 'a body of exactly 1 MB').toBeLessThan(300); }); diff --git a/packages/services/service-storage/README.md b/packages/services/service-storage/README.md index d40683d4acc..b40cee4e39d 100644 --- a/packages/services/service-storage/README.md +++ b/packages/services/service-storage/README.md @@ -110,7 +110,7 @@ doors whenever the plugin is bound to settings (the default): | Setting | Default | Applies to | |:---|:---|:---| -| `max_upload_mb` | 100 (MB, counted as MiB) | Every upload door: the presigned upload's declared `size`, the chunked upload's declared `totalSize`, the local raw PUT body and each chunk's running total. Over it ⇒ `413` with code `VALIDATION_ERROR`, before anything is stored. | +| `max_upload_mb` | 100 (MB, counted as MiB) | Every upload door: the presigned upload's declared `size`, the chunked upload's declared `totalSize`, the local raw PUT body and each chunk's running total. Over it ⇒ `413` with code `PAYLOAD_TOO_LARGE`, before anything is stored. | | `presigned_ttl` | 3600 s | Presigned upload URLs and non-gated download URLs issued from then on. | | `session_ttl` | 86400 s | Chunked upload sessions started from then on. | diff --git a/packages/services/service-storage/src/error-envelope.conformance.test.ts b/packages/services/service-storage/src/error-envelope.conformance.test.ts index 4a738725ccb..6817337e344 100644 --- a/packages/services/service-storage/src/error-envelope.conformance.test.ts +++ b/packages/services/service-storage/src/error-envelope.conformance.test.ts @@ -248,12 +248,12 @@ describe('storage error envelope (#3675)', () => { }, }, { - // #22283: the saved `max_upload_mb`, enforced at the upload doors — the - // standard member the platform derives for a 413 (no 413 member exists, - // and `PAYLOAD_TOO_LARGE` is registered under another package). + // #22283: the saved `max_upload_mb`, enforced at the upload doors — + // #22314: the ledger code registered for "too large" under this + // package's owner key, not the status-derived `VALIDATION_ERROR`. name: 'an upload declared over the saved max_upload_mb', status: 413, - code: 'VALIDATION_ERROR', + code: 'PAYLOAD_TOO_LARGE', run: async () => { const routes = mount(await tmpAdapter(), new StorageMetadataStore(null), { limitsSnapshot: () => ({ maxUploadMb: { value: 1, authored: true } }), diff --git a/packages/services/service-storage/src/storage-limits.test.ts b/packages/services/service-storage/src/storage-limits.test.ts index f593d40c537..83f1442de29 100644 --- a/packages/services/service-storage/src/storage-limits.test.ts +++ b/packages/services/service-storage/src/storage-limits.test.ts @@ -9,7 +9,7 @@ * - **§1 every upload door refuses over the limit, and stores nothing.** The * presigned door (declared `size`), the chunked start (declared * `totalSize`), the local raw PUT (the body) and the chunk door (the bytes - * received so far plus this chunk) each answer `413` / `VALIDATION_ERROR` + * received so far plus this chunk) each answer `413` / `PAYLOAD_TOO_LARGE` * in the ADR-0112 envelope, with no row written and no byte handed to the * adapter. Each has its control at exactly the limit, which is accepted. * - **§2 the TTLs and the precedence.** A saved TTL is what a presigned URL @@ -117,7 +117,7 @@ async function call( function expectTooLarge(answer: Answer): void { expect(answer.status).toBe(413); expect(answer.body?.success).toBe(false); - expect(answer.body?.error?.code).toBe('VALIDATION_ERROR'); + expect(answer.body?.error?.code).toBe('PAYLOAD_TOO_LARGE'); expect(String(answer.body?.error?.message)).toContain('max_upload_mb'); } @@ -148,7 +148,7 @@ const SAVED_ONE_MB: StorageLimitsSnapshot = { maxUploadMb: { value: 1, authored: // --------------------------------------------------------------------------- describe('[#22283] §1 · the upload doors refuse a file over max_upload_mb, before anything is stored', () => { - it('presigned door: a declared size over the limit is 413 VALIDATION_ERROR — no row, no URL minted', async () => { + it('presigned door: a declared size over the limit is 413 PAYLOAD_TOO_LARGE — no row, no URL minted', async () => { const { adapter, store, routes } = await mountDoors(SAVED_ONE_MB); const createFile = vi.spyOn(store, 'createFile'); const presign = vi.spyOn(adapter, 'getPresignedUpload'); diff --git a/packages/services/service-storage/src/storage-routes.ts b/packages/services/service-storage/src/storage-routes.ts index 2576ee00b73..f7e702b1019 100644 --- a/packages/services/service-storage/src/storage-routes.ts +++ b/packages/services/service-storage/src/storage-routes.ts @@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto'; import type { IHttpServer, IHttpRequest, IHttpResponse, IStorageService } from '@objectstack/spec/contracts'; -import type { StandardErrorCode } from '@objectstack/spec/api'; +import type { RegisteredErrorCode, StandardErrorCode } from '@objectstack/spec/api'; // The declared envelope is written in ONE place for the whole platform (#3973). import { sendOk, sendError } from '@objectstack/types'; // [#15999] The BRAND predicate, never `instanceof` — this error crosses package @@ -76,15 +76,21 @@ const ORGANIZATION_CHANGED_MESSAGE = * written. * * `413` because that is the condition (RFC 9110, content too large). - * `VALIDATION_ERROR` because it is the standard-catalog member the platform - * itself derives for a `413` (`standardErrorCodeForHttpStatus`, ADR-0112): the - * catalog names no `413` member, and the ledger's `PAYLOAD_TOO_LARGE` row is - * registered under `@objectstack/rest` alone, so stamping it here would be an - * unregistered emitter (`check:error-code-provenance`). The status carries the - * specific condition; the message names the limit and where it is set. + * `PAYLOAD_TOO_LARGE` because it is the one registered code for that condition + * (ADR-0112 ledger, listed under this package's owner key beside + * `@objectstack/rest`, which answers it for its import row limit): a client + * branches on "too large" by the code alone, at every door that refuses it. + * Not `VALIDATION_ERROR`, the bucket `standardErrorCodeForHttpStatus` derives + * for a `413`: the standard catalog names no `413` member, so that code would + * say only "malformed request" and leave the client to read the status. + * + * Typed `RegisteredErrorCode` so a misspelling, or the code leaving the + * ledger, fails to compile; that this package's own owner key lists it is + * `check:error-code-provenance`'s to hold. The message names the limit and + * where it is set. */ const UPLOAD_TOO_LARGE_STATUS = 413; -const UPLOAD_TOO_LARGE_CODE: StandardErrorCode = 'VALIDATION_ERROR'; +const UPLOAD_TOO_LARGE_CODE: RegisteredErrorCode = 'PAYLOAD_TOO_LARGE'; /** The refusal's message: what was measured, the limit, and the one setting that moves it. */ function uploadTooLargeMessage(measured: string, maxUploadBytes: number): string { diff --git a/packages/spec/src/api/error-code-ledger.zod.ts b/packages/spec/src/api/error-code-ledger.zod.ts index 959c47916c3..abbdc955116 100644 --- a/packages/spec/src/api/error-code-ledger.zod.ts +++ b/packages/spec/src/api/error-code-ledger.zod.ts @@ -511,6 +511,16 @@ export const ERROR_CODE_LEDGER = { 'INTERNAL', 'INVALID_REQUEST', 'INVALID_RESUME_TOKEN', + // [#22314] 413 from every upload door over the resolved `max_upload_mb` + // (#22283): the presigned door's declared `size`, the chunked start's + // declared `totalSize`, the local raw PUT body and each chunk's running + // total, by `content-length` before the body is read and by the bytes read + // — `storage-routes.ts`, refused before a row is written or a byte stored. + // Second EMITTER of the code `@objectstack/rest` already registers for its + // synchronous import row limit; one condition — the request carries more + // than the door accepts — one code a client branches on without reading + // the status. Provenance, not identity (see above). + 'PAYLOAD_TOO_LARGE', 'UPLOAD_SESSION_EXPIRED', // chunk/complete against a session past its own expires_at (#7667) 'UPLOAD_SESSION_NOT_FOUND', ], From 5b5b812ce958740650aced54d57219029adf90c3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 8 Oct 2026 21:20:27 +0000 Subject: [PATCH 2/2] fix(service-storage,spec): name both import row ceilings in the PAYLOAD_TOO_LARGE notes @objectstack/rest answers the code on both import routes, not only the synchronous one; the ledger row, the storage docblock and the spec note now say so. Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude --- .changeset/22314-storage-payload-too-large-provenance.md | 2 +- packages/services/service-storage/src/storage-routes.ts | 2 +- packages/spec/src/api/error-code-ledger.zod.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.changeset/22314-storage-payload-too-large-provenance.md b/.changeset/22314-storage-payload-too-large-provenance.md index 8cf44d76b65..ab220c4967f 100644 --- a/.changeset/22314-storage-payload-too-large-provenance.md +++ b/.changeset/22314-storage-payload-too-large-provenance.md @@ -6,6 +6,6 @@ feat(spec): the error-code ledger lists `PAYLOAD_TOO_LARGE` under `@objectstack/ Clause-②: yes (widening: a new owner provenance row in the published error-code ledger) -`ERROR_CODE_LEDGER['@objectstack/service-storage']` now lists `PAYLOAD_TOO_LARGE` (`413`), the code `@objectstack/rest` already registers for its import row ceiling. The storage upload doors answer it for an upload over the File Storage settings' `max_upload_mb`: the presigned upload's declared `size`, the chunked upload's declared `totalSize`, the local raw PUT body and each chunk's running total. +`ERROR_CODE_LEDGER['@objectstack/service-storage']` now lists `PAYLOAD_TOO_LARGE` (`413`), the code `@objectstack/rest` already registers for its import row ceilings. The storage upload doors answer it for an upload over the File Storage settings' `max_upload_mb`: the presigned upload's declared `size`, the chunked upload's declared `totalSize`, the local raw PUT body and each chunk's running total. No code is added. `ErrorCode`, `RegisteredErrorCode` and `REGISTERED_ERROR_CODES` are unchanged, so `ApiErrorSchema` accepts exactly what it accepted before. What widens is the per-package list a reader consults to learn which packages answer a code: a code emitted by several packages is listed under each of them. diff --git a/packages/services/service-storage/src/storage-routes.ts b/packages/services/service-storage/src/storage-routes.ts index f7e702b1019..1f094f04401 100644 --- a/packages/services/service-storage/src/storage-routes.ts +++ b/packages/services/service-storage/src/storage-routes.ts @@ -78,7 +78,7 @@ const ORGANIZATION_CHANGED_MESSAGE = * `413` because that is the condition (RFC 9110, content too large). * `PAYLOAD_TOO_LARGE` because it is the one registered code for that condition * (ADR-0112 ledger, listed under this package's owner key beside - * `@objectstack/rest`, which answers it for its import row limit): a client + * `@objectstack/rest`, which answers it for its import row ceilings): a client * branches on "too large" by the code alone, at every door that refuses it. * Not `VALIDATION_ERROR`, the bucket `standardErrorCodeForHttpStatus` derives * for a `413`: the standard catalog names no `413` member, so that code would diff --git a/packages/spec/src/api/error-code-ledger.zod.ts b/packages/spec/src/api/error-code-ledger.zod.ts index abbdc955116..dfd7b0ee218 100644 --- a/packages/spec/src/api/error-code-ledger.zod.ts +++ b/packages/spec/src/api/error-code-ledger.zod.ts @@ -517,9 +517,9 @@ export const ERROR_CODE_LEDGER = { // total, by `content-length` before the body is read and by the bytes read // — `storage-routes.ts`, refused before a row is written or a byte stored. // Second EMITTER of the code `@objectstack/rest` already registers for its - // synchronous import row limit; one condition — the request carries more - // than the door accepts — one code a client branches on without reading - // the status. Provenance, not identity (see above). + // import row ceilings (both import routes); one condition — the request + // carries more than the door accepts — one code a client branches on + // without reading the status. Provenance, not identity (see above). 'PAYLOAD_TOO_LARGE', 'UPLOAD_SESSION_EXPIRED', // chunk/complete against a session past its own expires_at (#7667) 'UPLOAD_SESSION_NOT_FOUND',