paperclipai/paperclip · error
runnerd exited before provider startup
Error message
runnerd exited before provider startup
What it means
Thrown from the provider-identity wait loop while polling for runnerd's provider identity evidence. On each 10ms iteration, after checking (and not yet finding) the expected provider identity event, the loop asks `#runnerHasExited()`; if the runnerd process has terminated before publishing its identity, this error is thrown instead of spinning until the deadline.
Source
Thrown at packages/paperclip-runner/src/live/runnerd-codex-transport.ts:5529
async #waitForProviderIdentity(
expectedEventType?: "harness.ready" | "session.started" | "session.resumed",
): Promise<void> {
const deadline = Date.now() + 30_000;
while (Date.now() < deadline) {
this.#throwIfFailed();
this.#pumpEvents();
if (
this.#threadId.length > 0 &&
(this.#checkpointProviderIdentityExpectation !== null ||
this.#evidence.providerExecutionKind === "remote_service" ||
this.#evidence.providerPid !== null) &&
(expectedEventType === undefined ||
this.#providerIdentityEventType === expectedEventType)
)
return;
if (await this.#runnerHasExited())
throw new Error("runnerd exited before provider startup");
await new Promise((resolveWait) => setTimeout(resolveWait, 10));
}
throw new Error("runnerd did not report its provider identity");
}
async #waitCommand(
type: string,
commandId?: string,
deadline = Date.now() + 30_000,
): Promise<void> {
while (Date.now() < deadline) {
this.#throwIfFailed();
const command =
commandId === undefined
? this.#core?.store.state.commands.find(
(candidate) => candidate.type === type,
)
: this.#core?.getCommand(commandId);View on GitHub (pinned to 01ad858492)
Solutions
- Run runnerd manually on the host to see its launch error (missing binary, bad flags, missing runtime).
- Check runnerd stderr/stdout captured by the transport for the crash reason.
- Verify the runnerd binary and codex runtime are installed and executable on the target platform.
- Restart the runner and retry; if startup consistently crashes, pin a known-good runnerd version.
Defensive patterns
Strategy: try-catch
Validate before calling
const alive = await runnerHasExited();
if (alive) throw new Error("runnerd is not running; start it before admitting turns"); Try / catch
try {
await transport.waitForProviderStartup();
} catch (err) {
if (err.message.includes("exited before provider startup")) {
await supervisor.restartRunnerd();
} else throw err;
} Prevention
- Health-check the runnerd binary and runtime before launch.
- Capture and ship runnerd stderr to logs for crash diagnosis.
- Run the supported OS/arch for the runnerd build.
When it happens
Trigger: Waiting for provider startup (optionally for a specific `expectedEventType`) when the runnerd process dies — crash on launch, bad runnerd binary, missing runtime dependencies, or an explicit kill — before it emits the provider identity evidence with a non-null `providerPid`.
Common situations: runnerd binary incompatible with the host OS/arch; missing codex runtime on the runner host; OOM or port conflict killing runnerd during startup; operator stopping the runner while a turn was being admitted.
Understand the failure class
Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.
Related errors
- runnerd did not report the provider turn identity
- runnerd did not report its provider identity
- ${label} has existing tables but no migration journal. Run m
- failed company transfer runs stranded in applying by a resta
- runnerd digest mismatch: expected ${request.runnerd.sha256},
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/1304f74ff0f8cf04.
Report an issue: GitHub.