#15606·activepieces

[BUG] Code step error text differs by AP_EXECUTION_MODE: "Error: " prefix and mangled properties only in the default mode

Author: majewskibartoszCreated Sep 17, 2026Updated Sep 17, 2026
Labels🐛 bug🔄 area/flows🛟 support

Problem

Same flow, same throw, different stored error text depending on the sandbox:

  • UNSANDBOXED (default) / SANDBOX_PROCESS -> "Error: Card declined"
  • SANDBOX_CODE_ONLY / SANDBOX_CODE_AND_PROCESS -> "Card declined"

With own properties on the Error (e.code = 'X') the default mode stores "Error: Card declined\n code: 'X'\n}" (inspect output with the opening brace eaten by stack stripping); the isolate mode stores "Card declined".

Any flow parsing {{step['error'].message}} written against one mode breaks on the other. Cloud and self-hosted can differ.

Ref: Pylon 6022

Root cause

packages/server/engine/src/lib/core/code/no-op-code-sandbox.tsCODE_RUNNER_SCRIPT sends the child's thrown value as inspect(e) (message plus stack plus own props as one string), and buildError wraps that string in a new host Error. formatPieceError then reads the whole inspect dump as the message; stripStack removes the \n at lines and leaves the Error: prefix and the props tail.

packages/server/engine/src/lib/core/code/v8-isolate-code-sandbox.ts — isolated-vm marshals only message and stack, so no prefix and no props.

Fix

Make the child process report the same shape the isolate does: send { message, stack } when the thrown value is an Error (keep inspect for non-Error throws), and have buildError set error.stack from it so raw still shows where in the user's code it threw.

Steps to reproduce

  1. Code step: exports.code = async () => { throw new Error('Card declined') }, "Continue on failure" on.
  2. Next step input: {{step_1['error'].message}}, run once with AP_EXECUTION_MODE=UNSANDBOXED, once with SANDBOX_CODE_ONLY.
  3. Observed: inner message is "Error: Card declined" vs "Card declined". Expected: identical.

Verification

Reproduced live in both modes on a full local stack at HEAD identical to the current cloud deploy.

Source: activepieces/activepieces