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
- Re-run the ACPX installation-verification step to rebuild the snapshot digests against the current executable.
- Pin the provider package version so installs don't silently swap the binary after snapshot creation.
- Delete node_modules/snapshot artifacts and reinstall to rule out a corrupted or partially-written executable.
- 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
- Re-run installation verification after upgrading provider packages (codex/claude-code)
- Pin provider package versions to avoid silent binary swaps
- Exclude snapshot/node_modules paths from antivirus and sync tools
- Rebuild snapshots rather than hand-editing their manifests
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
- ACPX snapshot manifest digest mismatch
- Materialized OpenCode executable digest mismatch
- Public viewer asset differs from trusted build: ${file}
- ACPX ${agent} runtime executable digest mismatch
- ACPX provider requires verified package snapshots
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/537127881c641d47.
Report an issue: GitHub.