{"record":{"id":"bc9c33aebac58d56","repo":"paperclipai/paperclip","slug":"sandbox-bridge-mode-requires-a-host-side-paperclip","errorCode":null,"errorMessage":"Sandbox bridge mode requires a host-side Paperclip API token.","messagePattern":"Sandbox bridge mode requires a host-side Paperclip API token\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/execution-target.ts","lineNumber":2058,"sourceCode":"  getRuntimeParentContext?: () => StartupSpanContext | undefined;\n  // Wrap each callback request in a `sandbox.callbackBridge.relayRequest` span.\n  // The factory threads it into the worker, which uses it per request so each\n  // request's execs group under one wrapper span. When it is absent, the request\n  // work runs under the run parent with no wrapper span.\n  runtimeSpan?: RuntimeSpanRunner;\n}): Promise<AdapterExecutionTargetPaperclipBridgeHandle | null> {\n  if (!adapterExecutionTargetUsesPaperclipBridge(input.target)) {\n    return null;\n  }\n  if (!input.target || input.target.kind !== \"remote\") {\n    return null;\n  }\n\n  const target = input.target;\n  const onLog = input.onLog ?? (async () => {});\n  const hostApiToken = input.hostApiToken?.trim() ?? \"\";\n  if (hostApiToken.length === 0) {\n    throw new Error(\"Sandbox bridge mode requires a host-side Paperclip API token.\");\n  }\n\n  const runtimeRootDir =\n    input.runtimeRootDir?.trim().length\n      ? input.runtimeRootDir.trim()\n      : path.posix.join(target.remoteCwd, \".paperclip-runtime\", input.adapterKey);\n  const bridgeRuntimeDir = path.posix.join(runtimeRootDir, \"paperclip-bridge\");\n  const queueDir = path.posix.join(bridgeRuntimeDir, \"queue\");\n  const assetRemoteDir = path.posix.join(bridgeRuntimeDir, \"server\");\n  const bridgeToken = createSandboxCallbackBridgeToken();\n  const maxBodyBytes =\n    typeof input.maxBodyBytes === \"number\" && Number.isFinite(input.maxBodyBytes) && input.maxBodyBytes > 0\n      ? Math.trunc(input.maxBodyBytes)\n      : DEFAULT_SANDBOX_CALLBACK_BRIDGE_MAX_BODY_BYTES;\n  const hostApiUrl =\n    input.hostApiUrl?.trim() ||\n    process.env.PAPERCLIP_RUNTIME_API_URL?.trim() ||\n    process.env.PAPERCLIP_API_URL?.trim() ||","sourceCodeStart":2040,"sourceCodeEnd":2076,"githubUrl":"https://github.com/paperclipai/paperclip/blob/67001ec6eb96ae601aa27bc91d9b2415d665334a/packages/adapter-utils/src/execution-target.ts#L2040-L2076","documentation":"Thrown by ensureAdapterExecutionTargetPaperclipBridge when the hostApiToken input is null, undefined, empty, or whitespace-only. The Paperclip callback bridge authenticates every sandbox-to-host request with this token, so an absent token makes the bridge non-functional and is rejected before any bridge infrastructure is created.","triggerScenarios":"Calling ensureAdapterExecutionTargetPaperclipBridge(input) where adapterExecutionTargetUsesPaperclipBridge(input.target) is true, input.target.kind === 'remote', and input.hostApiToken?.trim() yields an empty string.","commonSituations":"The Paperclip API token was not configured in the agent/company settings; the token environment variable (PAPERCLIP_API_TOKEN or similar) is unset in the host process; the token value was accidentally set to an empty string in configuration; the caller forgot to thread the token through to the bridge setup.","solutions":["Provide a valid hostApiToken in the ensureAdapterExecutionTargetPaperclipBridge input.","Verify the Paperclip API token is configured in the host process environment or agent configuration.","Check that the token is not being stripped or emptied by an intermediate configuration layer before reaching the bridge setup."],"exampleFix":"// before\nawait ensureAdapterExecutionTargetPaperclipBridge({\n  target,\n  adapterKey: \"claude\",\n  hostApiToken: process.env.MAYBE_TOKEN, // undefined when unset\n});\n// after\nawait ensureAdapterExecutionTargetPaperclipBridge({\n  target,\n  adapterKey: \"claude\",\n  hostApiToken: process.env.PAPERCLIP_API_TOKEN, // verified non-empty\n});","handlingStrategy":"validation","validationCode":"function ensureHostApiToken(token: string | null | undefined): string {\n  const trimmed = token?.trim() ?? \"\";\n  if (trimmed.length === 0) {\n    throw new Error(\"hostApiToken is required for sandbox bridge mode. Set PAPERCLIP_API_TOKEN or pass it explicitly.\");\n  }\n  return trimmed;\n}\n// Call before ensureAdapterExecutionTargetPaperclipBridge\nconst hostApiToken = ensureHostApiToken(process.env.PAPERCLIP_API_TOKEN);","typeGuard":"function hasHostApiToken(input: { hostApiToken?: string | null }): boolean {\n  return typeof input.hostApiToken === \"string\" && input.hostApiToken.trim().length > 0;\n}","tryCatchPattern":null,"preventionTips":["Always configure the Paperclip API token in the host process environment.","Validate the token is non-empty before calling bridge setup functions.","Use a secrets manager or devkey to inject the token rather than hardcoding."],"tags":["sandbox","bridge","auth","execution-target","adapter-utils"],"backgroundTag":null,"analyzedSha":"67001ec6eb96ae601aa27bc91d9b2415d665334a","analyzedAt":"2026-08-12T12:05:45.408Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}