paperclipai/paperclip · error · Error

ACPX runtime executable must be a bounded executable file

Error message

ACPX runtime executable must be a bounded executable file

What it means

When snapshotting the ACPX runtime executable itself, createAcpxPrivateSnapshot requires the file handle's stat to show a regular file with size between 1 byte and MAX_ACPX_RUNTIME_EXECUTABLE_BYTES. Otherwise it throws this error, refusing to bundle an empty, non-regular, or oversized runtime binary into the private snapshot.

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/private-snapshot.ts:189

      }
    }
    for (const root of roots)
      for (const pkg of packages) {
        const target = join(root, "node_modules", pkg.name);
        await mkdir(dirname(target), { recursive: true, mode: 0o700 });
        // Include generated directories in cleanup and read-only sealing.
        directories.push(join(root, "node_modules"), dirname(target));
        await symlink(pkg.root, target).catch(
          (error: NodeJS.ErrnoException) => {
            if (error.code !== "EEXIST") throw error;
          },
        );
      }
    let executablePath: string | null = null;
    if (executable) {
      const before = await executable.stat({ bigint: true });
      if (!before.isFile() || before.size < 1n || before.size > BigInt(MAX_ACPX_RUNTIME_EXECUTABLE_BYTES)) {
        throw new Error("ACPX runtime executable must be a bounded executable file");
      }
      // The bigint bound above makes this conversion exact before allocation.
      const executableBytes = Number(before.size);
      bytesCopied += executableBytes;
      if (bytesCopied > MAX_SNAPSHOT_BYTES) {
        throw new Error("ACPX snapshot exceeds its byte bound");
      }
      const bytes = await readSnapshotBytes(executable, executableBytes);
      if (!same(before, await executable.stat({ bigint: true })))
        throw new Error("ACPX executable changed during snapshot");
      executablePath = join(directory, "runtime");
      await writeFile(executablePath, bytes, { flag: "wx", mode: 0o500 });
      digests[executablePath] = digest(bytes);
    }
    const manifest = Buffer.from(
      JSON.stringify({ roots, executable: executablePath, digests }),
    );
    const manifestPath = join(directory, "manifest.json");

View on GitHub (pinned to 01ad858492)

Solutions

  1. Verify the executable path resolves to the real, fully-built runtime binary (check file size is nonzero and plausible)
  2. Reinstall/rebuild the ACPX runtime if the binary is truncated or empty
  3. If you ship a legitimately larger runtime, adjust MAX_ACPX_RUNTIME_EXECUTABLE_BYTES in the runner configuration consciously
  4. Ensure the path points at a regular file, not a script wrapper that is a symlink chain, fifo, or directory

Example fix

// before: placeholder stub
executablePath: './bin/acpx-stub' // 0 bytes
// after: real built binary
executablePath: './dist/bin/acpx' // regular file within bound
Defensive patterns

Strategy: validation

Validate before calling

const st = await fs.stat(executablePath);
if (!st.isFile() || st.size < 1 || st.size > MAX_ACPX_RUNTIME_EXECUTABLE_BYTES) {
  throw new Error(`invalid runtime executable at ${executablePath}: size=${st.size}`);
}

Type guard

function isValidRuntimeExecutable(st: { isFile(): boolean; size: number }): boolean {
  return st.isFile() && st.size >= 1 && st.size <= MAX_ACPX_RUNTIME_EXECUTABLE_BYTES;
}

Try / catch

try {
  await createAcpxPrivateSnapshot({ roots, executable });
} catch (e) {
  if (e.message.includes('runtime executable must be a bounded executable file')) {
    throw new Error(`Rebuild or reconfigure the ACPX runtime at ${executable.path}; it is empty, special, or oversized`);
  }
  throw e;
}

Prevention

When it happens

Trigger: createAcpxPrivateSnapshot is called with an `executable` whose stat({bigint:true}) reports isFile() === false, size === 0n, or size > MAX_ACPX_RUNTIME_EXECUTABLE_BYTES — i.e. the resolved ACPX runtime binary is missing content, a special file, or abnormally large.

Common situations: Misconfigured executable path pointing at a wrapper/stub or empty placeholder; a partially downloaded or truncated runtime binary; a custom runtime build that ballooned past the configured byte limit; pointing at a directory or device node by mistake.

Understand the failure class

Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/daf36c1ee867534c. Report an issue: GitHub.