{"record":{"id":"ebbc62c6e6bf953f","repo":"paperclipai/paperclip","slug":"sandbox-callback-bridge-wrote-invalid-readiness-js","errorCode":null,"errorMessage":"Sandbox callback bridge wrote invalid readiness JSON: ${error instanceof Error ? error.message : String(error)}","messagePattern":"Sandbox callback bridge wrote invalid readiness JSON: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"packages/adapter-utils/src/sandbox-callback-bridge.ts","lineNumber":1103,"sourceCode":"      \"    exit 1\",\n      \"  fi\",\n      \"  i=$((i + 1))\",\n      \"  sleep 0.05\",\n      \"done\",\n      `echo \"Timed out waiting for bridge readiness.\" >&2`,\n      `if [ -s ${shellQuote(directories.logFile)} ]; then cat ${shellQuote(directories.logFile)} >&2; fi`,\n      \"exit 1\",\n    ].join(\"\\n\"),\n    timeoutMs,\n    shellCommand,\n  );\n  requireSuccessfulResult(\"wait for sandbox callback bridge readiness\", readyResult);\n\n  let readyData: { host?: string; port?: number; baseUrl?: string; pid?: number };\n  try {\n    readyData = JSON.parse(readyResult.stdout.trim()) as { host?: string; port?: number; baseUrl?: string; pid?: number };\n  } catch (error) {\n    throw new Error(\n      `Sandbox callback bridge wrote invalid readiness JSON: ${error instanceof Error ? error.message : String(error)}`,\n    );\n  }\n\n  const host = typeof readyData.host === \"string\" && readyData.host.trim().length > 0\n    ? readyData.host.trim()\n    : \"127.0.0.1\";\n  const port = typeof readyData.port === \"number\" && Number.isFinite(readyData.port) ? readyData.port : 0;\n  if (!port) {\n    throw new Error(\"Sandbox callback bridge did not report a listening port.\");\n  }\n  const baseUrl =\n    typeof readyData.baseUrl === \"string\" && readyData.baseUrl.trim().length > 0\n      ? readyData.baseUrl.trim()\n      : `http://${host}:${port}`;\n\n  return {\n    baseUrl,","sourceCodeStart":1085,"sourceCodeEnd":1121,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/sandbox-callback-bridge.ts#L1085-L1121","documentation":"Thrown after the sandbox callback bridge server starts and the readiness wait loop returns, when the contents of the ready file cannot be parsed as JSON. The orchestrator expects the remote node bridge process to write a {host, port, baseUrl, pid} object to a ready.json file; this error means that file's stdout/stdout payload was empty, truncated, or non-JSON (e.g. it contains a shell error or a node stack trace). It surfaces a corrupt or aborted bridge startup rather than a caller-input problem.","triggerScenarios":"Calling startSandboxCallbackBridge (or the prepare path that uses it): the readiness wait command exits 0 (requireSuccessfulResult passed) but readyResult.stdout is not valid JSON. Happens when stdout carries a partial line, a leading log message, or the bridge wrote ready.json with a syntax error. Also triggered if a concurrent process writes to the same stdout that the wait loop cats.","commonSituations":"Bridge node process emits a warning to stdout before writing ready JSON; a mismatched node version on the remote host throws before the JSON write; the ready.json was hand-edited or partially flushed; a remote shell prints a login banner that pollutes stdout captured by the wait loop.","solutions":["Inspect the captured readiness payload: log readyResult.stdout.trim() right before JSON.parse to see the exact non-JSON bytes.","Check the remote bridge log file (directories.logFile) and stderr — the node bridge startup error is usually printed there.","Verify the remote node binary works (nodeCommand resolves on the sandbox host and matches a supported version) and that PAPERCLIP_BRIDGE_QUEUE_DIR/PAPERCLIP_BRIDGE_TOKEN are set for the bridge process.","Ensure no shell login banner or wrapper prints to stdout between the wait loop's `cat ready.json` and exit."],"exampleFix":"// before\nreadyData = JSON.parse(readyResult.stdout.trim()) as {...};\n// after — diagnose the payload before failing\nconst raw = readyResult.stdout.trim();\ntry {\n  readyData = JSON.parse(raw) as {...};\n} catch (error) {\n  throw new Error(\n    `Sandbox callback bridge wrote invalid readiness JSON: ${error instanceof Error ? error.message : String(error)}\\nPayload was: ${raw}`,\n  );\n}","handlingStrategy":"validation","validationCode":"// Before starting the bridge, validate the remote node can emit valid JSON.\n// After start, inspect stdout before parsing:\nconst raw = readyResult.stdout.trim();\nif (!raw.startsWith('{') || !raw.endsWith('}')) {\n  throw new Error(`Bridge readiness payload is not a JSON object: ${raw.slice(0, 200)}`);\n}\nconst readyData = JSON.parse(raw);","typeGuard":"function isBridgeReadyData(v: unknown): v is { host?: string; port?: number; baseUrl?: string; pid?: number } {\n  return typeof v === 'object' && v !== null;\n}","tryCatchPattern":"try {\n  readyData = JSON.parse(readyResult.stdout.trim()) as {...};\n} catch (error) {\n  // Include raw payload in the surfaced error for fast diagnosis.\n  throw new Error(`Bridge readiness JSON invalid: ${(error as Error).message}; payload=${readyResult.stdout.slice(0, 500)}`);\n}","preventionTips":["Run a smoke test of the generated bridge server source on the target node version before relying on it.","Keep the bridge exec channel stdout free of banners/profile output.","Log readyResult.stdout at debug level during integration tests."],"tags":["sandbox","bridge","json-parse","remote-execution","startup"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}