Skip to content

[Decision] A sandboxed CRASH carrying a declared code is answered by the arm's status and the QuickJS wrapper prose, where the unwrap door answers the same crash with a sanitised 500 #15071

Description

@os-trump

Filed by the domain:cli seat out of #14704 (PR #15065). ⭐ This is the follow-up card triage reserved by name, in its own words: "⛔ Do not decide the crash question. If a sandboxed crash (isScriptFaultMessage, #7543) reaches a code-gated arm, leave today's behaviour exactly as it is, implement the business-message read only, and name the site and the divergence in the report … ⛔ do not ask mid-run and ⛔ do not pick one."

The implementing dev did exactly that and recorded no recommendation, deliberately. So does this card: the analysis below is input, ⛔ not a lean.

The site

structuredCodeAnswer, and the PERMISSION_DENIED arm just below the consult — both reached from mapDataError in packages/rest/src/error-response.ts. Locate by symbol; line numbers in that file rot within the day.

The divergence, in wire shapes

the producer which door answers what the caller gets
sandboxed body crashes while carrying a declared code the code-gated arm that arm's status + the QuickJS wrapper prose — e.g. 409, code: DELETE_RESTRICTED, error: "hook 'guard' threw: TypeError: x is not a function"
sandboxed body crashes carrying no declared code classifyDataError's unwrap door the sanitised fault terminal — 500, UNCLASSIFIED_FAULT (#7543)

⇒ Two answers to the same event, chosen by whether the crashing body happened to declare a code. One leaks a stack-shaped internal sentence to an end user at a declared business status; the other is the sanitised terminal that exists precisely to stop that.

Behaviour today is unchanged by #14704, and the fence is mechanical

⭐ Worth stating because it is what makes deciding this later cheap: sandboxBusinessMessage declines a crash by contract, so the new armSentence(error) = sandboxBusinessMessage(error) ?? error?.message falls back to error.message and the answer does not move a byte. The fence holds by construction, not by anyone's intention.

And it is pinned as an ACCEPTED DIVERGENCE in error-response-sandbox-arm-message.test.ts §4, with a positive control (the same crash carrying no declared code does reach the sanitised 500). ⇒ Whatever this card decides, the change will be a visible edit to a named case, never a drift.

The options

A — status quo. An arm gated on a declared code keeps answering a crash with its own status and the wrapper prose. What PR #15065 ships, unchanged.

B — a crash outranks the arm. A sandboxed crash reaches the unwrap door's terminal (sanitised 500) whatever code it declares, matching the rule the unwrap door already applies one branch down. Consistent, and it removes the leak — at the cost of an author who declared DELETE_RESTRICTED and then crashed no longer seeing their declared status.

C — split the two decisions. The arm's status, with the prose withheld: fault classification and status declaration answered separately. The most precise and the only one that adds a mechanism.

Why it is a decision and not a fix

It is fault classification, not message sourcing. The question underneath is whether a declared code is a statement about this response or about this failure mode — and the two doors currently answer it differently on purpose-by-accident. That is a ruling about what the wire means, so it belongs in the maintainer's inbox rather than in a dev's judgement.

⚠️ Whoever takes it should note the population: a declared code plus a crash is the ordinary shape of an app author's hook that validates, declares a refusal, and then has a bug on a later line. It is not exotic.

Refs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions