paperclipai/paperclip · critical · Error

ACPX private snapshot digest mismatch

Error message

ACPX private snapshot digest mismatch

What it means

Thrown by the verifySnapshotBytes helper in the child-process bootstrap. When a private snapshot exists and a provider runtime executable is in use (providerRuntimeExecutableCount === 1), the executable bytes are read and sha256-hashed and compared to the digest recorded in the privateSnapshot manifest at privateSnapshot.digests[path]. A mismatch means the executable file's contents differ from what was verified at snapshot creation — a tamper/corruption guard on the provider binary itself.

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts:1680

  return [
    'const fs = require("node:fs");',
    'const { isBuiltin, registerHooks } = require("node:module");',
    'const { dirname, extname, join, normalize, relative, resolve } = require("node:path");',
    'const { fileURLToPath, pathToFileURL } = require("node:url");',
    "const commandDirectory = process.argv[1];",
    "const commandName = process.argv[2];",
    "const dependencyAncestorCount = Number.parseInt(process.argv[3], 10);",
    "const serverDependencyAncestorCount = Number.parseInt(process.argv[4], 10);",
    "const serverPackageFormat = process.argv[5];",
    "const dependencyAncestorFormats = JSON.parse(process.argv[6]);",
    "const providerRuntimeExecutableCount = Number.parseInt(process.argv[7], 10);",
    `const providerRuntimeEnvironmentVariable = process.env.${VERIFIED_PROVIDER_RUNTIME_TARGET_ENV};`,
    `delete process.env.${VERIFIED_PROVIDER_RUNTIME_TARGET_ENV};`,
    `const snapshotHandoff = process.platform === "darwin" ? JSON.parse(process.env.${ACPX_PRIVATE_SNAPSHOT_ENV} || "null") : null;`,
    'let privateSnapshot = null; if (snapshotHandoff) { const manifest = fs.readFileSync(snapshotHandoff.path); if (require("node:crypto").createHash("sha256").update(manifest).digest("hex") !== snapshotHandoff.digest) throw new Error("ACPX snapshot manifest digest mismatch"); privateSnapshot = JSON.parse(manifest); }',
    `delete process.env.${ACPX_PRIVATE_SNAPSHOT_ENV};`,
    'if (process.platform !== "linux" && !(process.platform === "darwin" && privateSnapshot && Array.isArray(privateSnapshot.roots) && privateSnapshot.roots.length === dependencyAncestorCount + 1)) throw new Error("ACPX provider requires verified package snapshots");',
    'const verifySnapshotBytes = (path, bytes) => { if (privateSnapshot && require("node:crypto").createHash("sha256").update(bytes).digest("hex") !== privateSnapshot.digests[path]) throw new Error("ACPX private snapshot digest mismatch"); };',
    'if (privateSnapshot && providerRuntimeExecutableCount === 1) verifySnapshotBytes(privateSnapshot.executable, fs.readFileSync(privateSnapshot.executable));',
    `if (!Number.isSafeInteger(dependencyAncestorCount) || dependencyAncestorCount < 0 || dependencyAncestorCount > ${MAX_DEPENDENCY_ANCESTORS}) throw new Error("ACPX provider dependency ancestry is invalid");`,
    'if (!Number.isSafeInteger(serverDependencyAncestorCount) || serverDependencyAncestorCount < 0 || serverDependencyAncestorCount > dependencyAncestorCount) throw new Error("ACPX provider package ancestry is invalid");',
    'if ((serverPackageFormat !== "module" && serverPackageFormat !== "commonjs") || !Array.isArray(dependencyAncestorFormats) || dependencyAncestorFormats.length !== dependencyAncestorCount || dependencyAncestorFormats.some((value) => value !== "module" && value !== "commonjs")) throw new Error("ACPX provider package formats are invalid");',
    'if (providerRuntimeExecutableCount !== 0 && providerRuntimeExecutableCount !== 1) throw new Error("ACPX provider runtime executable count is invalid");',
    `const providerRuntimeExecutableFd = ${DEPENDENCY_ANCESTOR_FD_START} + dependencyAncestorCount;`,
    'if (providerRuntimeExecutableCount === 1) { if (providerRuntimeEnvironmentVariable !== "CODEX_PATH" && providerRuntimeEnvironmentVariable !== "CLAUDE_CODE_EXECUTABLE") throw new Error("ACPX provider runtime environment target is invalid"); fs.fstatSync(providerRuntimeExecutableFd); process.env[providerRuntimeEnvironmentVariable] = privateSnapshot ? privateSnapshot.executable : "/proc/" + process.pid + "/fd/" + providerRuntimeExecutableFd; } else if (providerRuntimeEnvironmentVariable !== undefined) throw new Error("ACPX provider runtime environment target is unexpected");',
    ...(guarded
      ? [
          `const guardianFd = ${DEPENDENCY_ANCESTOR_FD_START} + dependencyAncestorCount + providerRuntimeExecutableCount;`,
          'const guardian = fs.createReadStream("", { fd: guardianFd, autoClose: false });',
          `const reapCurrentProviderProcessGroup = ${reapCurrentProviderProcessGroup.toString()};`,
          "const killProviderProcess = process.kill.bind(process);",
          "const providerProcessId = process.pid;",
          "const exitProviderProcess = process.exit.bind(process);",
          "let guardianLost = false;",
          "const reapOnGuardianLoss = () => { if (guardianLost) return; guardianLost = true; reapCurrentProviderProcessGroup(killProviderProcess, providerProcessId, exitProviderProcess); };",
          'guardian.once("end", reapOnGuardianLoss);',

View on GitHub (pinned to 01ad858492)

Solutions

  1. Re-run the ACPX installation-verification step to rebuild the snapshot digests against the current executable.
  2. Pin the provider package version so installs don't silently swap the binary after snapshot creation.
  3. Delete node_modules/snapshot artifacts and reinstall to rule out a corrupted or partially-written executable.
  4. Investigate unexpected writers (antivirus, sync tools) if the binary keeps changing under the snapshot.
Defensive patterns

Strategy: validation

Validate before calling

import { createHash } from "node:crypto";
const bytes = fs.readFileSync(privateSnapshot.executable);
const digest = createHash("sha256").update(bytes).digest("hex");
if (digest !== privateSnapshot.digests[privateSnapshot.executable]) {
  throw new Error("provider executable changed; rebuild snapshot before spawn");
}

Prevention

When it happens

Trigger: On darwin (or any platform using private snapshots) spawning a provider with providerRuntimeExecutableCount === 1 where sha256(fs.readFileSync(privateSnapshot.executable)) !== privateSnapshot.digests[privateSnapshot.executable].

Common situations: The provider binary (e.g. codex or claude-code executable) was updated/upgraded after the snapshot was built; npm/pnpm reinstalled or hoisted a different version; a cache or antivirus modified the file; the snapshot manifest is stale relative to node_modules.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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