paperclipai/paperclip · error

Unable to create a Linux-safe Paperclip sandbox proxy socket

Error message

Unable to create a Linux-safe Paperclip sandbox proxy socket directory.

What it means

Thrown by createNetworkProxyTempDir after both candidate base directories (/tmp and os.tmpdir()) fail to yield a directory whose proxy.sock path fits within 107 bytes, or when mkdtemp itself fails on both. The original failure is attached via { cause: lastError } so callers can inspect why both candidates failed.

Source

Thrown at packages/adapter-utils/src/local-process-sandbox.ts:194

async function createNetworkProxyTempDir(): Promise<string> {
  const candidates = Array.from(new Set(["/tmp", os.tmpdir()]));
  let lastError: unknown;
  for (const baseDir of candidates) {
    try {
      const tempDir = await fs.mkdtemp(path.join(baseDir, NETWORK_PROXY_TEMP_PREFIX));
      try {
        assertUnixSocketPathLength(path.join(tempDir, "proxy.sock"));
        return tempDir;
      } catch (error) {
        await fs.rm(tempDir, { recursive: true, force: true });
        lastError = error;
      }
    } catch (error) {
      lastError = error;
    }
  }
  throw new Error("Unable to create a Linux-safe Paperclip sandbox proxy socket directory.", { cause: lastError });
}

function parseTrustedNetworkUrl(value: string): NetworkAllowlistRule | null {
  try {
    const parsed = new URL(value);
    if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
    return {
      hostname: parsed.hostname.toLowerCase(),
      port: parsed.port || (parsed.protocol === "https:" ? "443" : "80"),
    };
  } catch {
    return null;
  }
}

function writeProxyError(response: http.ServerResponse, status: number, code: string, message: string): void {
  const body = `${JSON.stringify({ error: { code, message } })}\n`;
  response.writeHead(status, {

View on GitHub (pinned to 67001ec6eb)

Solutions

  1. Inspect err.cause — it tells you whether both candidates hit a length limit, a permission error, or disk-full.
  2. Set TMPDIR to a short writable path that is NOT the same as the failing /tmp candidate: export TMPDIR=/var/tmp or a dedicated /run/paperclip owned by the runner user.
  3. Ensure the runner user has write + create permissions on /tmp (mode 1777) and on the configured TMPDIR.
  4. Free inodes/disk on the tmpfs backing /tmp (df -h /tmp; df -i /tmp) — mkdtemp fails with ENOSPC when either is exhausted.

Example fix

// before: both /tmp and TMPDIR unusable
// err.cause === EACCES on /tmp, EACCES on /var/run/user/1000/long/...

// after: dedicated short, writable dir
await fs.mkdir("/run/paperclip", { recursive: true, mode: 0o700 });
process.env.TMPDIR = "/run/paperclip";
const target = await buildLocalProcessSandboxSpawnTarget(input);
Defensive patterns

Strategy: try-catch

Validate before calling

async function preflightTempDirs(): Promise<void> {
  const candidates = Array.from(new Set(["/tmp", process.env.TMPDIR ?? "/tmp"]));
  for (const base of candidates) {
    try {
      const probe = await fs.mkdtemp(path.join(base, "paperclip-preflight-"));
      const sock = path.join(probe, "proxy.sock");
      if (Buffer.byteLength(sock) > 107) throw new Error(`socket path too long under ${base}`);
      await fs.rm(probe, { recursive: true, force: true });
      return; // at least one candidate works
    } catch (error) {
      // try next
    }
  }
  throw new Error("No usable temp dir for sandbox; set TMPDIR to a short, writable path.");
}

Try / catch

try {
  return await buildLocalProcessSandboxSpawnTarget(input);
} catch (error) {
  if (error instanceof Error && error.message.startsWith("Unable to create a Linux-safe")) {
    const cause = (error as Error & { cause?: unknown }).cause;
    throw new ConfigError(`Sandbox temp dir unavailable. Cause: ${String(cause)}`, { cause: error });
  }
  throw error;
}

Prevention

When it happens

Trigger: Both /tmp and TMPDIR are unwritable, both are mounted on paths long enough that the generated paperclip-network-sandbox-XXXXXXXX/proxy.sock path exceeds 107 bytes, or both mkdtemp calls throw (ENOSPC, EROFS, EACCES). The function tries /tmp first then os.tmpdir(); only when both fail (or both produce oversized paths) does this throw.

Common situations: Hardened containers where /tmp is read-only or tmpfs-size-limited, CI runners where TMPDIR is set to a long path AND /tmp is overridden to the same long path, or sandbox environments where the process has no write access to either candidate. The cause field typically holds EACCES, ENOSPC, or a sibling assertUnixSocketPathLength error.

Related errors


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