Repository navigation
fix(service-automation): a try_catch whose catch region fails carries the refusal it met - #27929
Conversation
… the refusal it met The catch-region failure return re-words the handler's failure into a new returned failure, and the engine's carried-refusal brand on the handler's throw did not cross it, so a refusal met inside a catch region reached the flow's caller as text only. Spread readCarriedRefusal(catchErr) onto that return, the same carriage the no-catch return already uses. The carried refusal is the catch region's own, never the try region's it handled; a handler that met a fault carries neither member. The error text is unchanged. Pinned in flow-inner-refusal.test.ts through engine.execute; the inner-refusal.ts header's carrier list now names this path. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 7 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3fcb02edf3836d8a99182c86bad14e36be08b2e1 && git checkout 3fcb02edf3836d8a99182c86bad14e36be08b2e1
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin d4064cc159ce0804091278eb964d8da8e05dec87 ca881be3e6cb4c1f23742cdac6e7dac23ecf6bcd && git checkout -B drift-repro d4064cc159ce0804091278eb964d8da8e05dec87 && git merge --no-ff ca881be3e6cb4c1f23742cdac6e7dac23ecf6bcd
node scripts/docs-audit/affected-docs.mjs --json d4064cc159ce0804091278eb964d8da8e05dec87 |
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL 维护者速读
席位意见:ACCEPT。 席位核对Head-sha:
|
domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL
Fixes #27876
维护者速读
try_catch的 catch 区域自身失败时,返回的失败现在带上 catch 区域遇到的拒绝(innerRefusal/userMessage),沿用无 catch 路径已有的readCarriedRefusal一行承载,无新词汇。try_catchwith no catch region, or by amapitem /subflowchild that pauses and fails on resume, may not reach the flow's caller as the structured refusal #27743 的 catch 区域那一半。error文本逐字不变;带的是 catch 区域自己的拒绝,绝不是它已处理的 try 区域的拒绝;catch 区域遇到故障时两个成员都不出现;回滚即还原本 PR 的单个提交。packages/services/service-automation/src/builtin/try-catch-node.ts中 catch 区域失败的 return(一行展开 + 注释)。pnpm --filter @objectstack/service-automation exec vitest run src/flow-inner-refusal.test.ts,看#27876下三条用例通过(29/29)。Clause-②: no
Metadata-protocol: none
What changes
packages/services/service-automation/src/builtin/try-catch-node.ts: the catch-region failure return spreadsreadCarriedRefusal(catchErr), the same carriage the no-catch return already spreads (...lastCarried). The carried members are the ones the catch region's own throw carried. Theerrortext andchildStepsare unchanged.packages/services/service-automation/src/inner-refusal.ts: header only. The carrier list now names this path, and the paragraph that called it "Not a carrier" is removed.packages/services/service-automation/src/flow-inner-refusal.test.ts: three pins throughengine.execute(try = [create_record], catch = [create_record]):innerRefusal: { code: 'VALIDATION_FAILED', message: 'Amount must be positive' }and nouserMessage, anderrorkeeps its chain byte for byte.RESOURCE_CONFLICTand itsuserMessage, never the try region'sPERMISSION_DENIED..changeset/27876-try-catch-catch-region-carry-refusal.md:patchfor@objectstack/service-automation.Why
PR #27872 made a refusal met inside a
try_catchwith no catch region reach the flow's caller as the structured refusal. When the catch region itself failed, the return still re-worded the handler's failure into a new one. The engine's carried-refusal brand on the handler's throw does not cross that re-wording, so the caller got text only and a runner had no business message to show. This uses the same one-line carriage and adds no new vocabulary.Verification
All readings are against
objectstack-ai/objectstackat base2a3dec2d, in worktree branchwip-27876.engine.execute, without the fix):status: failed,error: "Node 'guard' failed: try_catch 'guard': catch region failed — Node 'b' failed: create_record(crm_account) failed: Amount must be positive",'innerRefusal' in result === false,'userMessage' in result === false.errortext byte for byte, plusinnerRefusal: { code: 'VALIDATION_FAILED', message: 'Amount must be positive' }.userMessagestays absent because a validation rule marks none.flow-inner-refusal.test.tshad 2 failed and 27 passed (29). Pins 1 and 2 failed withexpected undefined to deeply equal { code: … }. Pin 3, the control, passed, as a control should.git restore --source=HEAD --staged --worktree. Itsgit hash-object(09436a8a…) is the same before the ablation, after the restore, and inHEAD.@objectstack/service-automationpnpm testpassed 218 files and 2806 tests.pnpm typecheck(tsc pluscheck:test-typecheck) exited 0. Rootpnpm lintexited 0.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 59 commands for the four changed paths. I ran each one and recorded its exit code before any pipe. 58 exited 0. 1 is NOT MEASURED:pnpm check:dual-build-cjs-loadsexited 3 withPREREQUISITE NOT METbecause it reads every package's builtdist/, which needs a whole-tree build that was not run here.--ranreconciliation: 59 derived, 58 run, 1 NOT-MEASURED, 0 UNRUN. The derivation is identical byte for byte when taken from the tree atorigin/mainf552fa81, which is 12 commits past the base, and none of those commits touchservice-automation. Not measured locally: the CI-only steps the tool lists (Build Core, the Test Core shard plan, and the push-only Dogfood and Temporal jobs), the 4 families that take a workflow value, and the 11 families with a wide population.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4d1Np3hU7RQnA13YZcL