paperclipai/paperclip · error · Error

Installing or updating Paperclip requires Node.js ${MINIMUM_

Error message

Installing or updating Paperclip requires Node.js ${MINIMUM_NODE_VERSION} or newer (found ${process.version} at ${process.execPath}). Put a supported Node bin directory first on PATH and run 'npx paperclipai@latest install --yes' to re-pin an existing managed install.

What it means

This error is thrown by the runnerd codex transport when a PRP (provider run protocol) run rotation requires rotating an EXTERNAL authority epoch, but the transport options do not supply the callback functions needed to read and archive external runner state. The library refuses to guess identity rotation state and fails fast rather than proceeding without durable external state bookkeeping.

Source

Thrown at cli/src/commands/install.ts:89

    for (const section of ["dependencies", "optionalDependencies", "peerDependencies"] as const) {
      const dependencies = packageJson[section];
      if (!dependencies || typeof dependencies !== "object") continue;
      for (const dependencyName of Object.keys(dependencies)) {
        if (dependencyName.startsWith("@paperclipai/")) visit(dependencyName);
      }
    }
    visiting.delete(packageName);
    visited.add(packageName);
    ordered.push(entry);
  };

  visit("@paperclipai/server");
  return ordered;
}

export function assertSupportedNodeVersion(): void {
  if (!isSupportedNodeVersion(process.versions.node)) {
    throw new Error(`Installing or updating Paperclip requires Node.js ${MINIMUM_NODE_VERSION} or newer (found ${process.version} at ${process.execPath}). Put a supported Node bin directory first on PATH and run 'npx paperclipai@latest install --yes' to re-pin an existing managed install.`);
  }
}

export function resolveNpmInstallRequest(options: InstallOptions): {
  spec: string;
  channel: InstallChannel;
} {
  if (options.canary && options.version) throw new Error("Choose either --canary or --version, not both.");
  if (options.version) {
    const version = options.version.trim();
    if (!EXACT_VERSION_PATTERN.test(version)) {
      throw new Error(`--version requires an exact published version, received '${options.version}'.`);
    }
    return { spec: version, channel: "pinned" };
  }
  return options.canary ? { spec: "canary", channel: "canary" } : { spec: "latest", channel: "latest" };
}

View on GitHub (pinned to 01ad858492)

Solutions

  1. Provide both `readRunnerState` and `archiveExternalRunnerState` callbacks in the transport options before enabling PRP run rotation.
  2. If the runner manages state internally instead, arrange the rotation path to use the local authority epoch rotation (localStateOwner) rather than the external path.
  3. Check the transport construction code for conditional spreading of options that drops these callbacks.

Example fix

// before
const transport = new RunnerdCodexTransport({ archiveExternalRunnerState });
// after
const transport = new RunnerdCodexTransport({
  readRunnerState: async () => loadRunnerState(),
  archiveExternalRunnerState: async () => archiveState(),
});
Defensive patterns

Strategy: validation

Validate before calling

if (!options.readRunnerState || !options.archiveExternalRunnerState) {
  throw new Error("external state callbacks required for PRP run rotation");
}

Type guard

function hasExternalStateCallbacks(o: unknown): o is TransportOptions & {
  readRunnerState: () => Promise<unknown>;
  archiveExternalRunnerState: () => Promise<void>;
} {
  const x = o as TransportOptions;
  return typeof x.readRunnerState === "function" && typeof x.archiveExternalRunnerState === "function";
}

Try / catch

try {
  await transport.start(params);
} catch (e) {
  if (e.message === "native_runner_prp_run_rotation_unavailable") {
    // rebuild transport with full external-state callback set and retry
  }
}

Prevention

When it happens

Trigger: A run-rotation path is taken where the runner's authority must be rotated under an external state owner, but the options object passed to the transport omitted `readRunnerState` or `archiveExternalRunnerState` (one or both are `undefined`).

Common situations: Constructing the transport with a partial options object after a refactor; wiring an internal/local-state runner without providing external state callbacks; version changes that added newly required callback fields to the options contract.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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