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

  1. Run runnerd manually on the host to see its launch error (missing binary, bad flags, missing runtime).
  2. Check runnerd stderr/stdout captured by the transport for the crash reason.
  3. Verify the runnerd binary and codex runtime are installed and executable on the target platform.
  4. 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

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


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