Skip to content

Commit fa7a2e0

Browse files
committed
docs(analytics): err.code 的线上落点是 error.code,不是 error.details.code (#6123)
`errorResponseBase` 只把 `err.code` **暂存**进 `details`;共享构造器 `buildApiError`(`packages/runtime/src/error-envelope.ts:117`)随后经 `splitSemanticCode` 把它**提升**进 `ApiErrorSchema` 声明的 `error.code`, 并把已经清空的 `details` 作为 `undefined` 返回 —— 于是整个 `details` 键 被省略,`error.details.code` 在线上从不存在。 两处已合入的说明文字指向了这个不存在的键,一并更正并各补一句提升机制: - `.changeset/analytics-read-scope-compile-failed-500.md`(#5367 的 changeset, 已合入未发布,会随 `CHANGELOG.md` 发到 npm); - `packages/services/service-analytics/src/read-scope-sql.ts` 文件头。 纯文字/注释修正,零行为变化:两文件均未改动任何可执行代码行。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
1 parent 01faeb1 commit fa7a2e0

2 files changed

Lines changed: 26 additions & 4 deletions

File tree

.changeset/analytics-read-scope-compile-failed-500.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ the disclosure question to be re-decided message by message).
6767
`@objectstack/service-analytics` (ADR-0112 D3) and typed as
6868
`RegisteredErrorCode` at the constructor, so an unregistered code is a compile
6969
error. It is legible on the wire through the sibling `/analytics/query` exit,
70-
which puts a thrown `err.code` in `error.details.code` (#3842).
70+
which puts a thrown `err.code` at **`error.code`** (#3842) — read it there.
71+
`errorResponseBase` only stages the code inside a `details` object;
72+
`buildApiError` then runs `splitSemanticCode`, which promotes it into the
73+
declared `error.code` field and drops the now-empty `details`, so the key is
74+
omitted from the body and `error.details.code` is never present:
75+
`{"success":false,"error":{"code":"READ_SCOPE_COMPILE_FAILED","message":"Internal server error","httpStatus":500}}`.
7176

7277
**Which inputs are refused did not change.** No refusal condition moved: nothing
7378
that used to lower now throws, and nothing that used to throw now lowers. That is

packages/services/service-analytics/src/read-scope-sql.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,26 @@ import { likePattern, LIKE_ESCAPE_CHAR } from './like-pattern.js';
123123
* remove; the route keys on the DECLARATION instead.
124124
*
125125
* The code is what a machine reads: `dispatcher-plugin.errorResponseBase`, the
126-
* sibling `/analytics/query` exit, puts a thrown `err.code` in
127-
* `error.details.code` (#3842), so `READ_SCOPE_COMPILE_FAILED` is legible there
128-
* without anyone parsing prose.
126+
* sibling `/analytics/query` exit, puts a thrown `err.code` on the wire at
127+
* `error.code` (#3842), so `READ_SCOPE_COMPILE_FAILED` is legible there without
128+
* anyone parsing prose.
129+
*
130+
* ⚠️ At `error.code` — NOT `error.details.code`, which is where this note
131+
* pointed until #6123 corrected it. `errorResponseBase` only STAGES the code in
132+
* a `details` object; `buildApiError` then runs `splitSemanticCode`
133+
* (`@objectstack/runtime`, `src/error-envelope.ts:117`), which PROMOTES it into
134+
* the declared `ApiErrorSchema` field and returns the now-empty `details` as
135+
* `undefined` — so the key is omitted from the body and `error.details.code` is
136+
* never present to read. The measured 500 body is exactly:
137+
*
138+
* ```json
139+
* {"success":false,"error":{"code":"READ_SCOPE_COMPILE_FAILED",
140+
* "message":"Internal server error","httpStatus":500}}
141+
* ```
142+
*
143+
* Pinned end-to-end in `@objectstack/runtime`'s
144+
* `analytics-query-read-scope-withhold.test.ts`, which asserts the code at
145+
* `error.code` against a real `AnalyticsService` on a real mounted route.
129146
*
130147
* ⚠️ Deliberately NOT a 4xx of any flavour, including a 422. Option A on the
131148
* decision card was `READ_SCOPE_INVALID` / 422 ("not your fault, not a crash");

0 commit comments

Comments
 (0)