Uh oh!
There was an error while loading. Please reload this page.
docs(api): narrow the client-SDK flow-rejection example's catch binding - #12501
Conversation
The `automation.execute` example inside the API-surface tour fence read `err.httpStatus` / `err.code` / `err.details?.errorMessage` / `err.details?.summary` off an untyped `catch` binding. Under `strict` that binding is `unknown`, so a reader copying it got four TS18046 errors — measured, before: 4x "'err' is of type 'unknown'". No guard was in scope where the block sat, and the tour fence's subject is the client surface rather than error handling, so the example moves out of the tour into its own "Flow execution errors" subsection under Error Handling, where it reuses the `isApiError` guard the section already declares. `details` is `unknown` on that shared shape because what rides in it is per-surface; the automation door's own artefacts (`errorMessage`, `summary`) are narrowed one step further, and only on FLOW_FAILED, which is the only arm that carries them. The tour keeps a pointer in its place, and the page's contributor note is updated to say what is now true of the page's three catch bindings. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
Follow-up wording pass on the same block — no change to the narrowing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
yinlianghui
commented
Aug 26, 2026
PM review — The census answered the question I actually askedI flagged that the card says "a third example" while the page has three The part that makes this more than a mechanical fix⭐ Applying Both members optional, and riding the Also verified: Moving the example was the right call, and it is the call I asked forMy Zone 2B said a second narrowing idiom on one page is worse than one imperfect one. The Two disclosures I want on the record
Landing condition
Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#12388
The
automation.executeflow-rejection example — the page's third and lastunnarrowed
catch— readerr.httpStatus/err.code/err.details?.errorMessage/err.details?.summaryoff an untyped binding.Under
strictthat binding isunknown, so a reader copying it got fourTS18046s while the paragraph above it was telling them to branch on theerror's
code.Census first — which of the page's three
catchsites was actually unnarrowedThe card names "a third example"; the page has three
catchsites. Measured onthe base (
abc12cb07c, which already contains the### Error Codesrewrite)::617Error Handlingcatch (error)if (!isApiError(error)) throw error;:653per-field validationcatch (error):434API-surface tour,automation.executecatch (err)Exactly one was unnarrowed, and it is the one the card names. The premise
holds; no widening beyond it was needed.
Why the example moved instead of gaining a guard in place
Narrowing it where it sat was not available: the guard it should reuse
(
isApiError, declared by the first Error Handling block) lives ~160 linesbelow the tour fence, and the tour fence's own subject is the client surface,
not error handling. Inventing a second narrowing idiom inside the tour would
have been worse than one imperfect one, so the example now stands alone under
Error Handling as Flow execution errors, where the guard is already in
scope and reading order is forward. The tour keeps a pointer in its place.
The
detailsreads needed a second step, and the producer says whyApplying
isApiErroralone would have left two of the four reads broken:ObjectStackApiError.detailsis declaredunknown, because what rides in it isper-surface. Read at the producer rather than assumed —
packages/runtime/src/domains/automation.tsbuilds the refusal body as— so both members are optional, and they ride the
FLOW_FAILEDarm only: aflow that never dispatched has no author text and no node log to point at. The
example narrows
detailsone step further with a tested guard (no cast) andreads it only under
err.code === 'FLOW_FAILED', which is what the produceractually guarantees.
Consistent with the two-tier vocabulary the
### Error Codestable nowpublishes:
FLOW_DISABLED/FLOW_NO_START_NODE/FLOW_FAILEDare all inERROR_CODE_LEDGERand none is inStandardErrorCode(grepped inpackages/spec/src/api/), so the new prose says they are ledger-registeredrather than unofficial, and does not narrow
codeto the closed enum.check:skill-examplestype-checks only fences carrying anos:checkmarker.The three marked fences on this page are Quick Start, the filter-builder chain
and the React Hooks block; every Error Handling block is unmarked, and this PR
adds no marker — marker coverage for this page is a separate, blocked subject
and is deliberately untouched. So CI green below says nothing about this
example. What stands behind it is a local measurement against the gate's own
tsc profile (
strict,target ES2020,libES2020+DOM,types: [],moduleResolution: bundler, frompackages/spec/scripts/check-skill-examples.ts),with
clientandorderIddeclared in the harness so the page's separateTS2304 continuation convention is excluded and only the diagnostics under test
remain:
Before — the block as it stood at
abc12cb07c:4 — exactly the four reads, and exactly the count predicted when the two
sibling blocks were narrowed.
After — the guard block and the new block extracted from the committed
file:
tsc --noEmitexits0, zero diagnostics.Scope
Declared surface was the example block. Also touched, declared here rather than
left silent:
catchis inside the API-surface tour block (
automation.execute)" — false as ofthis PR, and a stale rationale sitting above the fences it describes is a trap
for the next author. Rewritten to say what is now true, including that the
block is still unmarked. Its historical 13-fence sweep figure is kept and
dated instead of restated, since this PR splits a fourteenth fence out of the
tour.
### Error Codessection is untouched,and no stylistic sweep was made.
Verification
Gate union re-derived from the change set on the final tree with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(noSTALE TREE; 23 families, byte-identical to the derivation on the previoushead), then all 23 run at
e437cc1c6f, each exit code captured before anypipe. All 23 green, including
pnpm --filter @objectstack/spec run check:skill-examples→✅ 260 prose examples type-check across 3 surface(s)— the same 260 as before,which is what an unmarked new fence should do.
Repo-wide
pnpm lintwas not run, and could not have said anything aboutthis diff. Asked through eslint's own config resolution rather than guessed:
Population: eslint's own resolution, not a hand-built list. Count: 0 lintable
files in this diff. Invariance: with no configuration applying to the path there
is no type-aware linting, so this diff cannot move an untouched file's verdict.
No changeset:
content/releases nothing, so this PR carries theskip-changesetlabel instead.Generated by Claude Code