paperclipai/paperclip · critical · Error

PAPERCLIP_BRIDGE_TOKEN is required.

Error message

PAPERCLIP_BRIDGE_TOKEN is required.

What it means

Thrown at top level of the zero-dependency bridge server that getSandboxCallbackBridgeServerSource() generates; the script runs INSIDE the sandbox and refuses to start unless PAPERCLIP_BRIDGE_TOKEN is set in its environment. The token is the shared secret the sandbox uses to authenticate callbacks to the host, so a missing token cannot be defaulted or ignored.

Source

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

    stream.on("response", (headers) => {
      const rawStatus = headers[":status"];
      status = typeof rawStatus === "number" ? rawStatus : Number(rawStatus) || 502;
      responseHeaders = {};
      for (const [key, value] of Object.entries(headers)) {
        if (key.startsWith(":") || value == null) continue;
        responseHeaders[key] = Array.isArray(value) ? value.join(", ") : String(value);
      }
    });
    stream.on("data", (chunk: Buffer) => chunks.push(chunk));
    stream.once("end", () => settle(() => resolve({ status, headers: responseHeaders, body: Buffer.concat(chunks) })));
    stream.once("error", (error) =>
      settle(() => reject(error instanceof Error ? error : new Error(String(error)))),
    );
    stream.once("aborted", () => settle(() => reject(new Error("Bridge HTTP/2 stream aborted."))));
    if (request.body.length > 0) {
      stream.end(request.body);
    } else if (!stream.writableEnded) {
      stream.end();
    }
  });
}

/**
 * Create the sandbox HTTP/2 client gateway. It opens one HTTP/2 client
 * session on the transport `createConnection` returns, and forwards each
 * local request the caller hands it (already checked against the bridge
 * token — see {@link SandboxHttp2BridgeGatewayRequest.receivedToken}) as one
 * HTTP/2 stream. It keeps the header allowlist on the sandbox side, exactly
 * as the file-mode gateway does.
 */
export function createSandboxHttp2BridgeGateway(
  options: CreateSandboxHttp2BridgeGatewayOptions,
): SandboxHttp2BridgeGateway {
  const authority = options.authority?.trim() || SANDBOX_HTTP2_GATEWAY_DEFAULT_AUTHORITY;
  const headerAllowlist = options.headerAllowlist ?? DEFAULT_SANDBOX_CALLBACK_BRIDGE_HEADER_ALLOWLIST;
  const session = http2.connect(`http://${authority}`, {

View on GitHub (pinned to 01ad858492)

Solutions

  1. Ensure the sandbox process env includes PAPERCLIP_BRIDGE_TOKEN with the token the host minted for this sandbox (pass it in the spawn options' env, merged with process.env).
  2. If you run the generated script manually, export a token first: PAPERCLIP_BRIDGE_TOKEN=$(paperclip ... ) node bridge.mjs.
  3. Check for an env-scrubbing wrapper (docker --env-file omission, su -l, set -u with unset vars) between the host launcher and the bridge process.
  4. Never log the token while debugging; verify presence with [ -n "$PAPERCLIP_BRIDGE_TOKEN" ] instead of printing it.

Example fix

// before
const child = spawn(process.execPath, [bridgeScriptPath], {
  env: { PAPERCLIP_BRIDGE_QUEUE_DIR: queueDir },
});

// after
const child = spawn(process.execPath, [bridgeScriptPath], {
  env: { ...process.env, PAPERCLIP_BRIDGE_QUEUE_DIR: queueDir, PAPERCLIP_BRIDGE_TOKEN: mintedToken },
});
Defensive patterns

Strategy: validation

Validate before calling

// Host side, before spawning the bridge inside the sandbox:
if (!bridgeToken) {
  throw new Error("refusing to start sandbox callback bridge: no token minted");
}
const env = { ...sandboxEnv, PAPERCLIP_BRIDGE_TOKEN: bridgeToken };

Try / catch

try {
  await startBridge(sandbox, token);
} catch (error) {
  if (error instanceof Error && error.message.includes("PAPERCLIP_BRIDGE_TOKEN")) {
    // env injection bug on the host side; re-mint and re-spawn, never start the bridge without it
    throw new Error("bridge env missing token; check sandbox spawn env injection");
  }
  throw error;
}

Prevention

When it happens

Trigger: Spawning the generated bridge source without PAPERCLIP_BRIDGE_TOKEN in env; a host-side launcher that fails to inject the minted token into the sandbox process env; manually copy-pasting the generated script into a shell for debugging without exporting the variable.

Common situations: Refactoring the sandbox spawn path and dropping the env entry; environment scrubbing (a wrapper that clears env before exec); debugging the bridge standalone outside Paperclip; note file mode additionally requires PAPERCLIP_BRIDGE_QUEUE_DIR (separate error) while duplex mode only needs the token.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-08-21). Data as JSON: /api/errors/30deb577576ca661. Report an issue: GitHub.