{"record":{"id":"ca4dad36b8083fc3","repo":"paperclipai/paperclip","slug":"acpx-provider-credential-fence-is-invalid","errorCode":null,"errorMessage":"ACPX provider credential fence is invalid","messagePattern":"ACPX provider credential fence is invalid","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts","lineNumber":1350,"sourceCode":"        const providerRuntimeExecutableCount =\n          providerRuntimeExecutable === null ? 0 : 1;\n        const providerGuardianFd =\n          DEPENDENCY_ANCESTOR_FD_START +\n          dependencyAncestors.length +\n          providerRuntimeExecutableCount;\n        const providerOwnershipFd = providerGuardianFd + 1;\n        const providerExitFd = providerOwnershipFd + 1;\n        if (\n          guarded &&\n          (!Array.isArray(lifetime.credentialFenceFds) ||\n            lifetime.credentialFenceFds.length !== 2 ||\n            lifetime.credentialFenceFds.some(\n              (fd) => !Number.isSafeInteger(fd) || fd < 0,\n            ) ||\n            lifetime.credentialFenceFds[0] === lifetime.credentialFenceFds[1] ||\n            typeof lifetime.activateCredentialFenceOwner !== \"function\")\n        ) {\n          throw new Error(\"ACPX provider credential fence is invalid\");\n        }\n        const runtimeTargetFd = guarded\n          ? providerExitFd + 3\n          : DEPENDENCY_ANCESTOR_FD_START +\n            dependencyAncestors.length +\n            providerRuntimeExecutableCount;\n        const runtimeHandoff =\n          verifiedRuntimeExecutableHandoff(runtimeTargetFd);\n        const environment = sanitizedNodeEnvironment(options.env);\n        delete environment[ACPX_PRIVATE_SNAPSHOT_ENV];\n        if (privateSnapshot) environment[ACPX_PRIVATE_SNAPSHOT_ENV] = JSON.stringify(privateSnapshot.handoff);\n        if (runtimeHandoff.environmentValue === undefined) {\n          delete environment[VERIFIED_RUNTIME_EXECUTABLE_ENV];\n        } else {\n          environment[VERIFIED_RUNTIME_EXECUTABLE_ENV] =\n            runtimeHandoff.environmentValue;\n        }\n        if (","sourceCodeStart":1332,"sourceCodeEnd":1368,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts#L1332-L1368","documentation":"Before spawning a guarded (credential-fenced) ACPX provider child process, the VerifiedAcpxProviderLifetime argument is validated. The throw fires when the lifetime lease passed as the third argument to spawn() is malformed: credentialFenceFds must be an array of exactly two distinct, non-negative safe integers, and activateCredentialFenceOwner must be a callable. The library throws this to fail fast rather than spawn a child with an unusable credential fence.","triggerScenarios":"Calling verifiedAcpxProvider.spawn(args, options, lifetime) where lifetime.credentialFenceFds is not an array, has length != 2, contains a non-safe-integer or negative fd, has both fds equal, or lifetime.activateCredentialFenceOwner is not a function.","commonSituations":"Hand-constructing a VerifiedAcpxProviderLifetime object instead of receiving it from the library's lease/fence factory; reusing a fence whose fds were already closed and reset to -1; passing a stub lifetime in tests; destructuring/serializing the lifetime (e.g. across a worker boundary) which turns the function into undefined.","solutions":["Obtain the lifetime from the library's own credential-fence creation path instead of constructing it manually.","Validate lifetime.credentialFenceFds: length 2, both Number.isSafeInteger and >= 0, and fds differ.","Ensure activateCredentialFenceOwner is passed as an actual function reference, not JSON-serialized or copied across a process boundary.","If the fence fds were closed/released, create a fresh fence rather than reusing the old lifetime."],"exampleFix":"// before\nawait provider.spawn(args, {}, { credentialFenceFds: [12, 12], activateCredentialFenceOwner: undefined });\n// after\nconst lifetime = createVerifiedAcpxProviderLifetime(); // library-provided\nawait provider.spawn(args, {}, lifetime);","handlingStrategy":"validation","validationCode":"function isValidLifetime(lt) {\n  return !!lt && Array.isArray(lt.credentialFenceFds) && lt.credentialFenceFds.length === 2 &&\n    lt.credentialFenceFds.every((fd) => Number.isSafeInteger(fd) && fd >= 0) &&\n    lt.credentialFenceFds[0] !== lt.credentialFenceFds[1] &&\n    typeof lt.activateCredentialFenceOwner === \"function\";\n}\nif (!isValidLifetime(lifetime)) throw new TypeError(\"lifetime must come from the credential fence factory\");","typeGuard":"const isValidLifetime = (lt): lt is VerifiedAcpxProviderLifetime =>\n  Array.isArray(lt?.credentialFenceFds) && lt.credentialFenceFds.length === 2 &&\n  lt.credentialFenceFds.every((fd) => Number.isSafeInteger(fd) && fd >= 0) &&\n  lt.credentialFenceFds[0] !== lt.credentialFenceFds[1] &&\n  typeof lt?.activateCredentialFenceOwner === \"function\";","tryCatchPattern":null,"preventionTips":["Always get lifetimes from the library's fence factory, never hand-built or JSON-serialized","Never pass a lifetime across process/worker boundaries (functions do not survive)","Create a fresh fence after closing a previous one instead of reusing fds"],"tags":["internal-invariant-violation","process-spawn","file-descriptor"],"backgroundTag":"invalid-argument-value","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"}