{"record":{"id":"5b20c19b1c84f3ed","repo":"paperclipai/paperclip","slug":"acpx-provider-runtime-executable-identity-changed","errorCode":null,"errorMessage":"ACPX provider runtime executable identity changed after verification","messagePattern":"ACPX provider runtime executable identity changed after verification","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts","lineNumber":724,"sourceCode":"        await currentDirectory.handle.close();\n        throw new Error(\n          \"ACPX provider executable directory identity changed after verification\",\n        );\n      }\n      let currentDependencyAncestors: FileHandle[] = [];\n      let currentRuntimeExecutable: FileHandle | null = null;\n      try {\n        currentDependencyAncestors =\n          await openDependencyAncestors(dependencyAncestors);\n        if (runtimeExecutable !== null) {\n          const current = await openVerifiedRuntimeExecutable(\n            runtimeExecutable.path,\n            runtimeExecutable.digest,\n            profile.agent,\n          );\n          if (!sameIdentity(current.identity, runtimeExecutable.identity)) {\n            await current.handle.close();\n            throw new Error(\n              \"ACPX provider runtime executable identity changed after verification\",\n            );\n          }\n          currentRuntimeExecutable = current.handle;\n        }\n        const current = await inspectCommand(\n          commandPath,\n          commandDigest,\n          \"provider\",\n        );\n        if (!sameIdentity(current.identity, commandIdentity)) {\n          current.bytes.fill(0);\n          throw new Error(\n            \"ACPX provider executable identity changed after verification\",\n          );\n        }\n        const privateSnapshot = process.platform === \"darwin\"\n          ? await createAcpxPrivateSnapshot([commandDirectory, ...dependencyAncestors.map((root) => root.path)], currentRuntimeExecutable)","sourceCodeStart":706,"sourceCodeEnd":742,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts#L706-L742","documentation":"openCommand re-opens the qualified runtime executable at launch and compares its freshly computed identity (device/inode plus digest metadata) with the identity captured during verifyQualifiedAcpxInstallation. If they differ, the runtime binary was replaced, re-linked, or re-created between verification and launch, so the lease is refused to prevent executing an unverified binary.","triggerScenarios":"Calling openCommand on a verified ACPX lease after openVerifiedRuntimeExecutable returns a handle whose sameIdentity(current.identity, runtimeExecutable.identity) is false — the file at runtimeExecutable.path changed its inode/identity after verification.","commonSituations":"Package manager or auto-updater replaced the binary between verify and open (e.g. npm install ran concurrently); the install directory was wiped and recreated; antivirus/quarantine rewrote the file; a container image rebuild raced the launch.","solutions":["Re-run the verification (installation) step to produce a fresh lease against the new binary, then openCommand again — do not reuse a stale lease.","Ensure no package manager/updater runs concurrently with agent launch; serialize installs and launches.","Check for processes rewriting files under the runtime package directory (updaters, AV, sync tools like Dropbox) and exclude the install path.","If this recurs, pin the install directory and reinstall the exact qualified runtime version so identities are stable."],"exampleFix":"// before\nconst lease = await verified.openCommand(); // binary replaced by concurrent npm install\n// after\nawait installPromise;            // wait for installs to finish\nconst lease = await reverified.openCommand(); // re-verify against current binary","handlingStrategy":"retry","validationCode":"const before = fs.statSync(runtimeExecutable.path, { bigint: true });\n// ... ensure no installers running ...\nconst after = fs.statSync(runtimeExecutable.path, { bigint: true });\nif (before.ino !== after.ino) throw new Error(\"runtime binary changed before open; re-verify first\");","typeGuard":null,"tryCatchPattern":"try {\n  lease = await verified.openCommand();\n} catch (e) {\n  if (e.message === \"ACPX provider runtime executable identity changed after verification\") {\n    verified = await verifyQualifiedAcpxInstallation(input); // fresh verification\n    lease = await verified.openCommand(); // single retry\n  } else throw e;\n}","preventionTips":["Serialize package installs and agent launches; never run npm/pnpm while an agent may open a lease.","Exclude the install directory from auto-updaters, antivirus rewrite, and file-sync tools.","Treat leases as single-shot: re-verify whenever the environment may have changed."],"tags":["installation-integrity","acpx","toctou","binary-identity"],"backgroundTag":"checksum-mismatch","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}