paperclipai/paperclip · error

ACPX provider runtime executable count is invalid

Error message

ACPX provider runtime executable count is invalid

What it means

Also thrown by the generated provider-lifetime watchdog script: process.argv[8] (providerRuntimeExecutableCount) must be exactly 0 or 1, because the watchdog's FD layout adds either zero or one descriptor for a provider runtime executable between the dependency-ancestor FDs and OWNER_FD. Any other value makes the FD arithmetic ambiguous, so the script throws immediately.

Source

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

owner.once("end", reap);
owner.once("error", reap);
owner.resume();
try {
  fs.writeSync(4, "armed\\n");
} catch {
  reap();
}
`;

export const PROVIDER_LIFETIME_GUARDIAN_SOURCE = `
const fs = require("node:fs");
const { spawn } = require("node:child_process");
const WATCHDOG_SOURCE = ${JSON.stringify(PROVIDER_LIFETIME_WATCHDOG_SOURCE)};
const runtimeExecutable = process.env.${VERIFIED_RUNTIME_EXECUTABLE_ENV} || process.execPath;
const dependencyAncestorCount = Number.parseInt(process.argv[4], 10);
const providerRuntimeExecutableCount = Number.parseInt(process.argv[8], 10);
if (!Number.isSafeInteger(dependencyAncestorCount) || dependencyAncestorCount < 0 || dependencyAncestorCount > ${MAX_DEPENDENCY_ANCESTORS}) throw new Error("ACPX provider dependency ancestry is invalid");
if (providerRuntimeExecutableCount !== 0 && providerRuntimeExecutableCount !== 1) throw new Error("ACPX provider runtime executable count is invalid");
const PROVIDER_RUNTIME_EXECUTABLE_FD = ${DEPENDENCY_ANCESTOR_FD_START} + dependencyAncestorCount;
const OWNER_FD = PROVIDER_RUNTIME_EXECUTABLE_FD + providerRuntimeExecutableCount;
const OWNERSHIP_FD = OWNER_FD + 1;
const PROVIDER_EXIT_FD = OWNERSHIP_FD + 1;
const CREDENTIAL_FENCE_FD_START = PROVIDER_EXIT_FD + 1;
const VERIFIED_RUNTIME_FD = CREDENTIAL_FENCE_FD_START + 2;
const dependencyAncestorFds = Array.from({ length: dependencyAncestorCount }, (_, index) => ${DEPENDENCY_ANCESTOR_FD_START} + index);
const runtimeDescriptorMatch = /^\\/proc\\/self\\/fd\\/([0-9]+)$/.exec(runtimeExecutable);
const runtimeDescriptorFd = runtimeDescriptorMatch === null ? null : Number.parseInt(runtimeDescriptorMatch[1], 10);
if (runtimeDescriptorFd !== null && runtimeDescriptorFd !== VERIFIED_RUNTIME_FD) throw new Error("ACPX verified runtime descriptor is misplaced");
if (runtimeDescriptorFd !== null) fs.fstatSync(runtimeDescriptorFd);
let provider;
let watchdog;
let reaped = false;
let shutdownStarted = false;
const reap = () => {
  if (reaped) return;
  reaped = true;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Inspect the spawn call and ensure argv[8] is exactly '0' or '1', derived via something like providerRuntimeExecutable ? 1 : 0.
  2. If a second provider runtime executable is genuinely required, update the generated watchdog layout to support count > 1 instead of passing the raw count.
  3. Confirm no refactor inserted/removed argv entries ahead of position 8, shifting the value the watchdog reads.
  4. Add a parent-side assertion (count === 0 || count === 1) before spawning to fail fast with a clearer message.

Example fix

// before: raw count may be >1 or non-numeric
spawn(process.execPath, [watchdogEntry, ..., String(runtimeExecutables.length), ...]);

// after: normalize to 0/1
const providerRuntimeExecutableCount = runtimeExecutables.length > 0 ? 1 : 0;
spawn(process.execPath, [watchdogEntry, ..., String(providerRuntimeExecutableCount), ...]);
Defensive patterns

Strategy: validation

Validate before calling

const providerRuntimeExecutableCount = runtimeExecutables.length > 0 ? 1 : 0;
if (providerRuntimeExecutableCount !== 0 && providerRuntimeExecutableCount !== 1) {
  throw new Error('provider runtime executable count must be 0 or 1');
}
// pass String(providerRuntimeExecutableCount) at argv position 8

Type guard

function isBinaryCount(n: unknown): n is 0 | 1 {
  return n === 0 || n === 1;
}

Try / catch

try {
  await spawnProviderWatchdog(args);
} catch (err) {
  if (err.message.includes('runtime executable count is invalid')) {
    // normalize the count and retry once
    args[8] = String(runtimeExecutables.length > 0 ? 1 : 0);
    await spawnProviderWatchdog(args);
  } else throw err;
}

Prevention

When it happens

Trigger: The spawning parent passes argv[8] as anything other than '0' or '1' — e.g. a boolean string ('true'), an undefined coerced to NaN via parseInt, the wrong argv slot after a refactor, or a computed count like the number of executables when the layout only supports 0 or 1.

Common situations: A code change added a second provider runtime executable without updating the watchdog layout; argument-position drift in the spawn call after inserting a new argv entry; passing a raw boolean/flag instead of the normalized 0/1 count; environment detection returning an unexpected executable count.

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/d84e5e2a38b98a19. Report an issue: GitHub.