paperclipai/paperclip · error · Error

ACPX provider credential fence is invalid

Error message

ACPX provider credential fence is invalid

What it means

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.

Source

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

        const providerRuntimeExecutableCount =
          providerRuntimeExecutable === null ? 0 : 1;
        const providerGuardianFd =
          DEPENDENCY_ANCESTOR_FD_START +
          dependencyAncestors.length +
          providerRuntimeExecutableCount;
        const providerOwnershipFd = providerGuardianFd + 1;
        const providerExitFd = providerOwnershipFd + 1;
        if (
          guarded &&
          (!Array.isArray(lifetime.credentialFenceFds) ||
            lifetime.credentialFenceFds.length !== 2 ||
            lifetime.credentialFenceFds.some(
              (fd) => !Number.isSafeInteger(fd) || fd < 0,
            ) ||
            lifetime.credentialFenceFds[0] === lifetime.credentialFenceFds[1] ||
            typeof lifetime.activateCredentialFenceOwner !== "function")
        ) {
          throw new Error("ACPX provider credential fence is invalid");
        }
        const runtimeTargetFd = guarded
          ? providerExitFd + 3
          : DEPENDENCY_ANCESTOR_FD_START +
            dependencyAncestors.length +
            providerRuntimeExecutableCount;
        const runtimeHandoff =
          verifiedRuntimeExecutableHandoff(runtimeTargetFd);
        const environment = sanitizedNodeEnvironment(options.env);
        delete environment[ACPX_PRIVATE_SNAPSHOT_ENV];
        if (privateSnapshot) environment[ACPX_PRIVATE_SNAPSHOT_ENV] = JSON.stringify(privateSnapshot.handoff);
        if (runtimeHandoff.environmentValue === undefined) {
          delete environment[VERIFIED_RUNTIME_EXECUTABLE_ENV];
        } else {
          environment[VERIFIED_RUNTIME_EXECUTABLE_ENV] =
            runtimeHandoff.environmentValue;
        }
        if (

View on GitHub (pinned to 01ad858492)

Solutions

  1. Obtain the lifetime from the library's own credential-fence creation path instead of constructing it manually.
  2. Validate lifetime.credentialFenceFds: length 2, both Number.isSafeInteger and >= 0, and fds differ.
  3. Ensure activateCredentialFenceOwner is passed as an actual function reference, not JSON-serialized or copied across a process boundary.
  4. If the fence fds were closed/released, create a fresh fence rather than reusing the old lifetime.

Example fix

// before
await provider.spawn(args, {}, { credentialFenceFds: [12, 12], activateCredentialFenceOwner: undefined });
// after
const lifetime = createVerifiedAcpxProviderLifetime(); // library-provided
await provider.spawn(args, {}, lifetime);
Defensive patterns

Strategy: validation

Validate before calling

function isValidLifetime(lt) {
  return !!lt && Array.isArray(lt.credentialFenceFds) && lt.credentialFenceFds.length === 2 &&
    lt.credentialFenceFds.every((fd) => Number.isSafeInteger(fd) && fd >= 0) &&
    lt.credentialFenceFds[0] !== lt.credentialFenceFds[1] &&
    typeof lt.activateCredentialFenceOwner === "function";
}
if (!isValidLifetime(lifetime)) throw new TypeError("lifetime must come from the credential fence factory");

Type guard

const isValidLifetime = (lt): lt is VerifiedAcpxProviderLifetime =>
  Array.isArray(lt?.credentialFenceFds) && lt.credentialFenceFds.length === 2 &&
  lt.credentialFenceFds.every((fd) => Number.isSafeInteger(fd) && fd >= 0) &&
  lt.credentialFenceFds[0] !== lt.credentialFenceFds[1] &&
  typeof lt?.activateCredentialFenceOwner === "function";

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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