Uh oh!
There was an error while loading. Please reload this page.
fix(runtime): PUT /automation/:name answers 400 VALIDATION_FAILED, not 500, matching POST (#8123) - #8177
Conversation
…t 500, matching POST (#8123) PUT /:name made the identical registerFlow call POST / makes, in the same file, with no classification around it — so the two doors disagreed about the class of an identical malformed-flow refusal after #8055 fixed POST alone. PUT now routes through the same module-local, route-agnostic flowDefinitionRefusal helper POST uses. Adds a cross-door parity suite that drives both routes with the same four bad bodies from #8055/#8123 and asserts the two responses agree on status, code, and details.fields[] shape — not just that PUT looks right in isolation, which is exactly the kind of pin that let the two drift apart the first time. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B3Kurx8qufrDzNjk4rag7V
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 20 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also reference the affected code. These are read-only:
|
hotlong
commented
Aug 12, 2026
PM review — |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#8123
What
PUT /api/v1/automation/:name(updateFlow) makes the identicalautomationService.registerFlow(name, definition)callPOST /makes, in the same file (packages/runtime/src/domains/automation.ts), with no classification around it. #8055 / PR #8125 reclassifiedPOST /'s refusal from 500INTERNAL_ERRORto 400VALIDATION_FAILEDthrough a module-local, route-agnosticflowDefinitionRefusalhelper — but leftPUTuntouched, so the two doors disagreed about the class of an identical malformed-flow refusal.PUT /:name'sregisterFlowcall is now wrapped the same way, reusing the sameflowDefinitionRefusalhelperPOST /uses:The helper itself is untouched —
POST /'s behaviour is unchanged.The pin — both doors compared directly
Added
packages/runtime/src/domains/automation-put-post-error-parity.test.ts: a table-driven suite that drives bothPOST /andPUT /:namewith the same four malformed bodies from #8055/#8123 (missinglabel, unknown node key, malformedtry_catchregion, undeclared #4277 config key) and asserts:status,code) plus ADR-0114details.fields[]shape — not just "not 500"status, samecode, samefields[], samemessage— so reverting either door's classification (not justPUT's) reddens this fileparse()their config, and tighten the undeclared-key warning into an error #4277 self-correcting message survives verbatim throughPUTtoo (names the key, the node, the node type, and the declared keys)PUTeitherPUT's own{ definition }/ bare-body dialect (body.definition ?? body) classifies identically either way.statusstill keeps it onPUT(the same escape hatchPOSThonours)Contrast controls: a well-formed body still registers 200 on both doors, and no previously-refused body starts succeeding on
PUT— only the class changed from 500 to 400.Reverse verification
Reverted the source fix only (
git checkout origin/main -- packages/runtime/src/domains/automation.ts, keeping the new test file) and re-ran the parity suite. 9 of 9PUT-touching tests went red, all for the expected reason — the raw thrown error now escapes uncaught from the unguarded call atautomation.ts:880, so the test's ownawaitrejects instead of observing a classified response, e.g.:The other 145 test files (2196 tests, including the untouched POST-only
#8055suite) stayed green throughout. Restored the fix viagit applyof the saved patch;git diff --stat HEADonautomation.tscame back identical before and after the round trip (13 insertions, 1 deletion both times).Verification
pnpm --filter '@objectstack/runtime^...' build— closure builds cleanpnpm --filter @objectstack/runtime test -- --maxWorkers=2— 146 test files, 2205 tests, all passpnpm --filter @objectstack/runtime typecheck— cleanpnpm check:type-check-debt(full closure built first) — green,@objectstack/runtimeun-drifted from its recorded 227node scripts/check-nul-bytes.mjs— OKNot in scope
Widening the refusal, changing which bodies are rejected, or altering
flowDefinitionRefusal's behaviour forPOST— none of that changed here. Did not touch#8054'sListRunsRequestSchema.limitbounds or#7968's/runs/:runId/screengating, both queued behind this card on the same file.Changeset
.changeset/put-automation-error-class-parity.md—@objectstack/runtimepatch, user-visible status-code change onPUT.Generated by Claude Code