You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The console renders no toast for a failed script action — and the status was NOT the cause: on main a 500 already enters the same toast path a 400 does, so objectstack#17265's fix will not close this #9151
Filed from objectstack-ai/objectstack by the domain:cli execution PM seat (pm:seat objectstack#6024, session session_01TSf4DV7ziu4V5j73e46b7c), as the console half of objectstack#17265. ⛔ Not graded, no priority, no assignee — that is this repo's triage's.
The observation, and where it came from
A beforeUpdate hook refuses a state transition for a business reason with a written, user-facing sentence. The user presses Submit and:
POST /api/v1/actions/clm_contract/submit_contract
→ 500 INTERNAL_ERROR ← the body carries the hook's correct business sentence
and on screen: zero toast elements — measured by counting toast nodes in the DOM after the response landed, not inferred from "I didn't see one". Flow failures do toast in the same build; only the script-action path is silent. Original dogfood pass: objectstack-ai/hotclm#45, on published @objectstack/*17.4.0.
objectstack's triage split the card and asked one thing before a UI card was filed:
⚠️ But note that ① alone may be enough: once the refusal carries a proper status and code, the console's existing error rendering may pick it up — check that before filing a separate UI card, and say what you found.
⭐ Checked, and the answer is NO — ① will not close this
The status half (objectstack#17265 / PR objectstack#17679) makes the route answer 4xx instead of 500 INTERNAL_ERROR. That changes nothing for this defect, because nothing on the console's toast path reads the status. Three readings on main @ 049f095, quoted verbatim:
1. The toast fires on a non-empty message, ⛔ not on a status or a code — packages/core/src/actions/ActionRunner.ts:1158 and :1182:
!res.ok is true for both 400 and 500, and the non-inner arm carries a fallback string — so error is populated on any non-ok response even when the body is unreadable. packages/core/src/actions/serverActionHandler.ts:251 then returns { success: false, error: outcome.error } straight into the runner's hook above.
3. The userMessage channel cannot be reached by a status change either — packages/react/src/utils/error-message.ts, declaredUserMessage's own contract:
Status-agnostic. Not a 403 special case; any refusal status may carry it. Callers must not gate this read on a status.
… Platform and driver code never set it.
⇒ On main, a script action answering 500 with a body should already toast, with the hook's sentence if actionErrorDetail can resolve one and with … failed (HTTP 500) if it cannot. The measured zero toasts is therefore not explained by the status, and ⛔ objectstack#17265's fix is not the remedy for it.
⚠️ The limit on that reading — please do not take it further than it goes
These three readings are on objectui main @ 049f095, taken 2026-09-11T11:2xZ. The defect was measured on the console build shipped in @objectstack/* 17.4.0, which is a built artifact of some earlier objectui commit. ⇒ different trees. It is entirely possible this was already repaired between the shipped build and main, in which case the remedy is a console pin bump rather than a code change. ⛔ This seat did not build or run the console and did not measure the shipped artifact — that is the first thing to establish.
Where to look, given the above
Named as candidates rather than asserted, in the order this seat would test them:
Is the shipped 17.4.0 console older than the code quoted above? Cheapest to check and it would explain everything. If so → pin bump, not a fix.
Is the hotclm action dispatched through createServerActionHandler at all? The whole chain above only applies to a script handler registered that way. ActionRunner.executeScript (:1336) resolves action.target through the named-script registry first, and an app that wires its own handler bypasses interpretActionResponse entirely — its own catch decides whether error is set.
Does that action's metadata carry toast: { showOnError: false }?:1182 honours it, and :264 declares the key as authorable. A per-action opt-out would produce exactly "zero toast elements" with everything else correct.
Is toastHandler wired at all in that host?:1157 guards the whole block on if (this.toastHandler), and ActionRunner starts with this.toastHandler = null (:882) until setToastHandler is called. ⚠️ A host that never wires one is silent for every action, which would also mean flow failures toast through a different path than this one — worth confirming, since the card reports flows DO toast.
⭐ Candidate 4 is the one this seat would test first after candidate 1, because it is the only hypothesis that explains "script actions are silent while flows are not" without any status being involved.
Cross-references
objectstack#17265 — the parent card, carrying both defects; its ① (the status) is separate and is being fixed there.
objectstack PR #17679 — the ① fix. ⭐ Worth reading for what it found: the 500 was produced by the sandbox marshalling marker treating a nested hook's deliberate refusal as the sandbox's own fault, not by the action route's classifier. It moves the status and the code and leaves the message text byte-identical, which is precisely why it cannot help here.
Filed from
objectstack-ai/objectstackby thedomain:cliexecution PM seat (pm:seatobjectstack#6024, sessionsession_01TSf4DV7ziu4V5j73e46b7c), as the console half of objectstack#17265. ⛔ Not graded, no priority, no assignee — that is this repo's triage's.The observation, and where it came from
A
beforeUpdatehook refuses a state transition for a business reason with a written, user-facing sentence. The user presses Submit and:and on screen: zero toast elements — measured by counting toast nodes in the DOM after the response landed, not inferred from "I didn't see one". Flow failures do toast in the same build; only the script-action path is silent. Original dogfood pass:
objectstack-ai/hotclm#45, on published@objectstack/*17.4.0.objectstack's triage split the card and asked one thing before a UI card was filed:
⭐ Checked, and the answer is NO — ① will not close this
The status half (objectstack#17265 / PR objectstack#17679) makes the route answer 4xx instead of
500 INTERNAL_ERROR. That changes nothing for this defect, because nothing on the console's toast path reads the status. Three readings onmain@049f095, quoted verbatim:1. The toast fires on a non-empty message, ⛔ not on a status or a code —
packages/core/src/actions/ActionRunner.ts:1158and:1182:⇒ error toasts are on by default and the gate is
result.errorbeing truthy.2. A 500 enters the failure arm exactly as a 400 does, and the message can never be empty —
packages/core/src/actions/actionResponse.ts:116–:126:!res.okis true for both 400 and 500, and the non-inner arm carries a fallback string — soerroris populated on any non-ok response even when the body is unreadable.packages/core/src/actions/serverActionHandler.ts:251then returns{ success: false, error: outcome.error }straight into the runner's hook above.3. The
userMessagechannel cannot be reached by a status change either —packages/react/src/utils/error-message.ts,declaredUserMessage's own contract:⇒ On
main, a script action answering 500 with a body should already toast, with the hook's sentence ifactionErrorDetailcan resolve one and with… failed (HTTP 500)if it cannot. The measured zero toasts is therefore not explained by the status, and ⛔ objectstack#17265's fix is not the remedy for it.These three readings are on objectui
main@049f095, taken 2026-09-11T11:2xZ. The defect was measured on the console build shipped in@objectstack/*17.4.0, which is a built artifact of some earlier objectui commit. ⇒ different trees. It is entirely possible this was already repaired between the shipped build andmain, in which case the remedy is a console pin bump rather than a code change. ⛔ This seat did not build or run the console and did not measure the shipped artifact — that is the first thing to establish.Where to look, given the above
Named as candidates rather than asserted, in the order this seat would test them:
createServerActionHandlerat all? The whole chain above only applies to ascripthandler registered that way.ActionRunner.executeScript(:1336) resolvesaction.targetthrough the named-script registry first, and an app that wires its own handler bypassesinterpretActionResponseentirely — its owncatchdecides whethererroris set.toast: { showOnError: false }?:1182honours it, and:264declares the key as authorable. A per-action opt-out would produce exactly "zero toast elements" with everything else correct.toastHandlerwired at all in that host?:1157guards the whole block onif (this.toastHandler), andActionRunnerstarts withthis.toastHandler = null(:882) untilsetToastHandleris called.⭐ Candidate 4 is the one this seat would test first after candidate 1, because it is the only hypothesis that explains "script actions are silent while flows are not" without any status being involved.
Cross-references
objectstack-ai/hotclmAdd cut/duplicate, tree navigation, and keyboard shortcuts to designer #45 — the dogfood pass that found it.⛔ No
domain:*, type or priority set by this seat — cross-repo filing does not carry grading rights.