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
- Inspect the spawn call and ensure argv[8] is exactly '0' or '1', derived via something like providerRuntimeExecutable ? 1 : 0.
- If a second provider runtime executable is genuinely required, update the generated watchdog layout to support count > 1 instead of passing the raw count.
- Confirm no refactor inserted/removed argv entries ahead of position 8, shifting the value the watchdog reads.
- 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
- Normalize the executable count to 0/1 at the spawn site rather than passing raw array lengths or booleans.
- Write a regression test that executes the generated watchdog script with boundary argv values (0 and 1).
- When changing the spawn argv layout, audit every argv[N] index consumed by the generated script in the same PR.
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
- ACPX provider dependency ancestry is invalid
- ACPX sidecar runtime context must be pre-materialized
- persisted Codex ACPX session identity is inconsistent
- ACPX runtime omitted acpxRecordId
- ACPX runtime omitted backendSessionId
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/d84e5e2a38b98a19.
Report an issue: GitHub.