paperclipai/paperclip · error
${input.label} sync wrote invalid result JSON: ${error insta
Error message
${input.label} sync wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)} What it means
Thrown by syncRemoteTextFileWithHashSkip after the remote upload script completed successfully (requireSuccessfulResult passed, exit code 0) but stdout could not be parsed as JSON with an uploaded field. The script prints {"uploaded":true} after a fresh upload, {"uploaded":false} after a sha256 short-circuit skip; any other stdout breaks JSON.parse and surfaces as this error with the parse failure attached.
Source
Thrown at packages/adapter-utils/src/sandbox-callback-bridge.ts:970
" exit 1",
" fi",
"else",
` echo ${shellQuote(`${input.label} sha verify skipped: no sha256sum/shasum on remote.`)} >&2`,
"fi",
"mv \"$remote_partial\" \"$remote_path\"",
"printf '{\"uploaded\":true}\\n'",
].join("\n"),
timeoutMs,
shellCommand,
base64Body,
);
requireSuccessfulResult(input.action, syncResult);
let uploaded = false;
try {
uploaded = JSON.parse(syncResult.stdout.trim())?.uploaded === true;
} catch (error) {
throw new Error(
`${input.label} sync wrote invalid result JSON: ${error instanceof Error ? error.message : String(error)}`,
);
}
return { uploaded, sha256 };
}
export async function syncSandboxCallbackBridgeEntrypoint(input: {
runner: CommandManagedRuntimeRunner;
remoteCwd: string;
assetRemoteDir: string;
bridgeAsset: SandboxCallbackBridgeAsset;
timeoutMs?: number | null;
shellCommand?: "bash" | "sh" | null;
}): Promise<{ remoteEntrypoint: string; sha256: string; uploaded: boolean }> {
const remoteEntrypoint = path.posix.join(input.assetRemoteDir, SANDBOX_CALLBACK_BRIDGE_ENTRYPOINT);
const entrypointSource = await fs.readFile(input.bridgeAsset.entrypoint, "utf8");
const { uploaded, sha256 } = await syncRemoteTextFileWithHashSkip({View on GitHub (pinned to 67001ec6eb)
Solutions
- Silence non-interactive shell noise on the remote: run bash --noprofile --norc -c '...' and remove stdout-echoing statements from .bashrc/.profile.
- Use the bridge's shellCommand option to force a clean shell ("bash" with --noprofile --norc, or "sh" for minimal remotes).
- Disable ssh MOTD/banner output for the runtime user (sshd config: PrintMotd no, Banner none; remove /etc/motd echoes).
- When debugging, log syncResult.stdout verbatim before JSON.parse — the raw text reveals the polluting line, which the wrapped error does not include.
Example fix
// before: remote /etc/profile echoes a welcome line to stdout
// echo "Welcome to $HOSTNAME" # delete or redirect to stderr
// after: pass a clean non-interactive shell to the bridge
syncSandboxCallbackBridgeEntrypoint({
runner, remoteCwd, assetRemoteDir, bridgeAsset,
shellCommand: "bash",
});
// and ensure the runtime user's startup files are silent non-interactively Defensive patterns
Strategy: validation
Validate before calling
async function preflightSyncShellIsSilent(input: { runner: CommandManagedRuntimeRunner; remoteCwd: string }): Promise<void> {
const result = await runShell(input.runner, input.remoteCwd, 'printf \'{"uploaded":true}\\n\'', 5000, "bash");
try {
if (JSON.parse(result.stdout.trim())?.uploaded !== true) {
throw new Error(`Unexpected stdout: ${result.stdout}`);
}
} catch (error) {
throw new Error(`Remote sync shell pollutes stdout; disable MOTD/.bashrc echoes: ${String(error)}`);
}
} Try / catch
try {
await syncSandboxCallbackBridgeEntrypoint(input);
} catch (error) {
if (error instanceof Error && /sync wrote invalid result JSON/.test(error.message)) {
throw new BridgeError("Sync remote shell wrote non-JSON to stdout. Suppress MOTD/.bashrc echoes or switch to bash --noprofile --norc.", { cause: error });
}
throw error;
} Prevention
- Pass shellCommand: "bash" (with --noprofile --norc startup) when constructing the sync input.
- Silence remote /etc/profile, /etc/motd, and sshd Banner output for the runtime user.
- Guard ~/.bashrc to skip non-interactive sessions.
- Log syncResult.stdout verbatim during debugging — the wrapped error omits the offending line.
When it happens
Trigger: Remote shell prints a banner, trace, or warning to stdout before the printf; sha256sum or shasum on the remote emits output that escapes the script's redirection discipline; or a partial run left stdout truncated. The parse at sandbox-callback-bridge.ts:968 throws, which is wrapped with input.label (e.g. "Sandbox callback bridge entrypoint") for context.
Common situations: Same family as 337: SSH MOTD, .bashrc echoes, set -x shell tracing, or PAM modules writing to stdout. Specific to this code path: missing hash tools on minimal remotes (Alpine/scratch) trigger the sha-verify-skipped branch but still print the expected JSON, so that path alone does not cause this error; the cause is stdout pollution or a truncated shell result.
Related errors
- Sandbox callback bridge response write wrote invalid result
- Timed out acquiring sandbox callback bridge response lock.
- Bridge response body exceeded the configured size limit of $
- Failed to parse JSON at ${filePath}: ${err instanceof Error
- --payload must be a JSON object
AI-assisted analysis of paperclipai/paperclip@67001ec6eb (2026-08-12).
Data as JSON: /api/errors/4c2d58a14bded9da.
Report an issue: GitHub.