fix(actions): one source for the /actions envelope rule, and redirectUrl finally works - #2967
Merged
Merged
Conversation
…Url finally works
objectstack#3913 follow-up. The /actions response wraps TWICE — the route's own
{success, data} inside the dispatcher's — and a failure has three shapes, only
one of which res.ok catches. That rule was hand-rolled in two places
(useConsoleActionRuntime.serverActionHandler and RecordDetailView's copy of the
same handler) and the two drifted. Four hand-rolled copies produced three
distinct bugs: a failed action reported as success (the green-toast symptom, and
the console's MAIN action path), a React #31 crash from the nested {message}
object reaching toast.error() as a child, and redirectUrl never firing.
Both handlers now call interpretActionResponse from utils/actionResponse, and a
ratchet test fails if a third hand-rolled copy appears.
redirectUrl was unreachable, not merely wrong. A script action can return
{ redirectUrl } to ask the console to open a URL; both handlers read it off
body.data — the ACTION envelope — but that level is constructed by the server
and only ever holds success/data, so body.data.redirectUrl was always undefined
and no handler could work around it. An opensInNewTab action was worse than a
no-op: it pre-opens a tab on a spinner page for popup-blocker safety, and with
no redirect to drive it to, that tab sat on the spinner forever.
ActionResult.data still carries the action envelope, unchanged — resultDialog
field paths in the wild may have adapted to that depth, so it is not silently
re-pointed here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011AvZj6cLX7APd7roh2eK4F
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
baozhoutao
added a commit
that referenced
this pull request
Jul 29, 2026
Only conflict: the import block in RecordDetailView — main added `interpretActionResponse` (#2967) on the line this branch added `decisionOutputParams`. Both kept; the two changes touch disjoint parts of the file (the action-envelope reader vs the approval decision actions). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #2963 (objectstack#3913). That PR fixed the symptom in the two places it appeared; this one removes the reason it could appear at all, and fixes a third bug found while doing so.
The rule existed twice, and drifted
The
/actionsresponse wraps twice — the route's own{success, data}inside the dispatcher's — and a failure has three shapes, only one of whichres.okcatches:That rule was hand-rolled in
useConsoleActionRuntime.serverActionHandlerand again inRecordDetailView's copy of the same handler. Four hand-rolled readings produced three distinct bugs:{message, code}object handed totoast.error()as a React child. (Fixed in fix(actions): a failed server action no longer reports as success (green toast) #2963.)redirectUrlnever fired — fixed here.Both handlers now call
interpretActionResponsefromutils/actionResponse, andactions-envelope.ratchet.test.tsfails if a third hand-rolled copy appears. The ratchet also asserts it is still scanning the two known callers, so it can't start passing vacuously if the files move.redirectUrlwas unreachable, not merely wrongA script action can return
{ redirectUrl: 'https://…' }to ask the console to open a URL. Both handlers read it offbody.data— the action envelope, one level too shallow:body.datais constructed by the server and only ever holdssuccess/data, sobody.data.redirectUrlwas always undefined. There was no handler-side workaround — that level isn't reachable from a handler's return value at all.An
opensInNewTabaction was worse than a no-op: it pre-opens a tab on a spinner page for popup-blocker safety, and with no redirect to drive it to, that tab sat on the spinner forever.What is deliberately NOT changed
ActionResult.datastill carries the action envelope, not the payload.resultDialogfield paths in the wild may have adapted to that depth (apiHandler's own comment describes exactly that class of workaround), so re-pointing it silently would break configs. Flagging it rather than doing it — happy to follow up if you want it corrected properly with a migration note.Testing
app-shellsuite: 1929 passed (234 files).type-checkclean; eslint 0 errors on changed files.utils/__tests__/actionResponse.test.ts(10 cases) pins all three failure shapes and the double-wrap read; one new case inuseConsoleActionRuntime.test.tsxdrivesredirectUrlend-to-end throughwindow.open.readActionPayloadto the old shallow read fails 3 of them, and they pass with it.🤖 Generated with Claude Code
https://claude.ai/code/session_011AvZj6cLX7APd7roh2eK4F
Generated by Claude Code