paperclipai/paperclip · error

Sandbox callback bridge response write wrote invalid result

Error message

Sandbox callback bridge response write wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}

What it means

Thrown by the command-managed writeResponseFile after the remote shell script completed successfully (requireSuccessfulResult passed, exit code 0) but stdout could not be parsed as JSON containing a wrote field. The script is expected to print {"wrote":true} or {"wrote":false}; anything else indicates the remote shell emitted unexpected text (banner, set -x trace, warning) that broke JSON.parse or did not contain wrote.

Source

Thrown at packages/adapter-utils/src/sandbox-callback-bridge.ts:607

          "if [ -f \"$response_path\" ]; then",
          "  printf '{\"wrote\":false}\\n'",
          "  exit 0",
          "fi",
          "cat > \"$temp_path\"",
          "mv \"$temp_path\" \"$response_path\"",
          "printf '{\"wrote\":true}\\n'",
        ].join("\n"),
        timeoutMs,
        shellCommand,
        body,
      );
      requireSuccessfulResult(`write bridge response ${responsePath}`, result);
      try {
        return {
          wrote: JSON.parse(result.stdout.trim())?.wrote === true,
        };
      } catch (error) {
        throw new Error(
          `Sandbox callback bridge response write wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`,
        );
      }
    },
    rename: async (fromPath, toPath) => {
      await runChecked(
        `rename ${fromPath}`,
        `mkdir -p ${shellQuote(path.posix.dirname(toPath))} && mv ${shellQuote(fromPath)} ${shellQuote(toPath)}`,
      );
    },
    remove: async (remotePath) => {
      await runChecked(`remove ${remotePath}`, `rm -rf ${shellQuote(remotePath)}`);
    },
  };
}

async function writeBridgeResponse(
  client: SandboxCallbackBridgeQueueClient,

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Suppress remote shell noise: ensure the bridge runs with a clean non-interactive shell (bash --noprofile --norc -c '...') and that no .bashrc echoes to stdout.
  2. Move any MOTD/banner output to stderr on the remote host (PAM, sshd config).
  3. Check the runner's shellCommand setting — prefer bash with --noprofile --norc to skip startup files that emit stdout.
  4. Capture the raw stdout in your error handler to see exactly what the remote printed; the message includes the parse error but not the raw stdout, so add a log of result.stdout before parsing when debugging.

Example fix

// before: remote ~/.bashrc echoes a greeting to stdout
// echo "Welcome"  # <-- delete or redirect to stderr

// after: keep startup files silent on non-interactive shells
// in ~/.bashrc
if [ -z "$PS1" ]; then return; fi  # skip rest for non-interactive
Defensive patterns

Strategy: validation

Validate before calling

async function preflightRemoteShellIsSilent(runner: CommandManagedRuntimeRunner, remoteCwd: string): Promise<void> {
  const result = await runShell(runner, remoteCwd, 'printf \'{"wrote":true}\\n\'', 5000, "bash");
  try {
    if (JSON.parse(result.stdout.trim())?.wrote !== true) {
      throw new Error(`Unexpected stdout: ${result.stdout}`);
    }
  } catch (error) {
    throw new Error(`Remote shell pollutes stdout; disable MOTD/.bashrc echoes: ${String(error)}`);
  }
}

Try / catch

try {
  await client.writeResponseFile(responsePath, body, { requestPath });
} catch (error) {
  if (error instanceof Error && error.message.startsWith("Sandbox callback bridge response write wrote invalid result JSON")) {
    throw new BridgeError("Remote shell wrote non-JSON to stdout. Suppress MOTD/.bashrc echoes or switch to bash --noprofile --norc.", { cause: error });
  }
  throw error;
}

Prevention

When it happens

Trigger: Remote SSH session prints a login banner before the script, the shell is run with -x tracing that pollutes stdout, a remote .bashrc echoes text on non-interactive shells, or a partial script timeout left stdout truncated. The JSON.parse call at sandbox-callback-bridge.ts:604 throws, which is wrapped into this error message including the underlying parse error.

Common situations: SSH banners from MOTD or PAM modules that echo to stdout instead of stderr; interactive shell configs (PS1, echo statements) sourced for non-interactive sessions; shells with set -v or set -x enabled globally; or PATH/locale warnings printed to stdout. The shell quote/redirect discipline in the script keeps stderr separate, but any stdout noise trips this guard.

Related errors


AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12). Data as JSON: /api/errors/cb687ca84bd95425. Report an issue: GitHub.