paperclipai/paperclip · error
native_adopted_runner_state_directory_required
native_adopted_runner_state_directory_required
Error message
native_adopted_runner_state_directory_required
What it means
The CapabilityRunnerdCodexTransport constructor enforces that when adoptExistingRunner is true, a non-empty stateDirectory must be supplied. Adopting an existing native runner requires knowing where its durable state lives; without it the transport cannot attach to prior state.
Source
Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:3209
#failure: Error | null = null;
readonly #failureSignal: Promise<never>;
#rejectFailureSignal!: (error: Error) => void;
#runnerRecoveryInProgress = false;
#startupComplete = false;
#startupFailureCode = "native_runner_process_exited";
#controlPlaneCheckpoint:
((settlement: "settled" | "unsettled") => Promise<void> | void) | null =
null;
#controlPlaneRelease: (() => Promise<void> | void) | null = null;
#nextTraceDebugSequence = 1;
#traceRehydrationSpoolOverflow = false;
#pendingTraceRehydrations: PendingTraceRehydration[] = [];
#pendingDriverTraceInterpretations: PendingDriverTraceInterpretation[] = [];
readonly #bridgedRuntimeInputs = new Map<string, { durableTurnId: string }>();
constructor(readonly options: CapabilityRunnerdCodexTransportOptions) {
if (options.adoptExistingRunner && !options.stateDirectory?.trim()) {
throw new Error("native_adopted_runner_state_directory_required");
}
if (options.provider === "acpx" && options.acpxAgent === "pi") {
throw new Error("The Pi ACPX profile is not available");
}
this.#failureSignal = new Promise<never>((_resolve, reject) => {
this.#rejectFailureSignal = reject;
});
// Failure is also observed by request/notification paths. Register an
// internal handler so a process exit after the owner has closed the
// session cannot become an unhandled process-level rejection.
void this.#failureSignal.catch(() => undefined);
this.#ownsRoot = options.stateDirectory === undefined;
this.#turnId = options.resumeActiveTurnId ?? "";
this.#root =
options.stateDirectory ??
mkdtempSync(resolve(tmpdir(), "paperclip-runner-lab-prp-"));
if (options.resumeDynamicTools !== undefined) {
this.#authorizedTools = authorizedToolSetForProvider(options.provider, [View on GitHub (pinned to 01ad858492)
Solutions
- Pass a non-empty stateDirectory when adoptExistingRunner is true, e.g. stateDirectory: '/var/lib/paperclip/runner'.
- Set the env/config value feeding stateDirectory (e.g. PAPERCLIP_RUNNER_STATE_DIR) before constructing the transport.
- If not adopting an existing runner, set adoptExistingRunner: false so the check is skipped.
- Trim/validate the value at config-load time to fail early with a clearer message.
Example fix
// before
new CapabilityRunnerdCodexTransport({ adoptExistingRunner: true });
// after
new CapabilityRunnerdCodexTransport({ adoptExistingRunner: true, stateDirectory: process.env.RUNNER_STATE_DIR ?? '/var/lib/paperclip/runner' }); Defensive patterns
Strategy: validation
Validate before calling
if (opts.adoptExistingRunner && !(opts.stateDirectory ?? '').trim()) {
throw new Error('stateDirectory is required when adoptExistingRunner is true');
} Type guard
const canAdopt = (o: { adoptExistingRunner?: boolean; stateDirectory?: string }): o is Required<typeof o> =>
!o.adoptExistingRunner || Boolean(o.stateDirectory?.trim()); Try / catch
try {
transport = new CapabilityRunnerdCodexTransport(options);
} catch (err) {
if ((err as Error).message === 'native_adopted_runner_state_directory_required') {
options.stateDirectory = resolveStateDir();
transport = new CapabilityRunnerdCodexTransport(options);
} else throw err;
} Prevention
- Validate transport options at config-load time
- Give stateDirectory a safe default in the environment setup
- Trim and check string config values before constructing transports
When it happens
Trigger: new CapabilityRunnerdCodexTransport({ adoptExistingRunner: true, stateDirectory: undefined }) or stateDirectory: '' / ' ' (whitespace-only is also rejected).
Common situations: Config object assembled conditionally where stateDirectory is only set for fresh runners; env var for state dir unset; trimming/empty-string defaults; copying options between adopt and non-adopt code paths.
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
- ${label} is not a regular file at ${canonical}.
- Claude Managed evals require exact model claude-sonnet-5
- AWS AgentCore evals require exact model global.anthropic.cla
- request.session.provider must match request.provider
- request.session.requestedModel must match request.model
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/d6d6123118605583.
Report an issue: GitHub.