{"record":{"id":"e1e48e6b6fddc3e7","repo":"paperclipai/paperclip","slug":"prp-provider-thread-is-not-started","errorCode":null,"errorMessage":"PRP provider thread is not started","messagePattern":"PRP provider thread is not started","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/live/runnerd-codex-transport.ts","lineNumber":5454,"sourceCode":"          durableRecoveryInternals.canonicalJson(payload)\n      ) {\n        throw new Error(\n          `PRP steering correlation ${correlationId} was reused with different content`,\n        );\n      }\n    } else {\n      core.queueCommand(type, payload, commandId, true);\n    }\n    await this.#waitCommand(type, commandId);\n  }\n\n  async #commandResult(\n    type: string,\n    payload: Record<string, unknown>,\n    deadline?: number,\n  ): Promise<Record<string, unknown>> {\n    const core = this.#core;\n    if (core === null) throw new Error(\"PRP provider thread is not started\");\n    const commandId = `command_lab_${randomUUID().replaceAll(\"-\", \"\")}`;\n    core.queueCommand(type, payload, commandId, true);\n    await this.#waitCommand(type, commandId, deadline);\n    const command = core.getCommand(commandId);\n    if (command?.status !== \"completed\" || command.type !== type) {\n      throw new Error(`PRP command ${type} omitted its durable result`);\n    }\n    const result = record(record(command.result).result);\n    const completion = this.#pendingWarmRecoveryCompletion;\n    if (completion && type === \"session.snapshot\") {\n      const expectation = this.#checkpointProviderIdentityExpectation;\n      const providerIdentity = resolveRunnerdSessionIdentity(result);\n      if (\n        command.type !== \"session.snapshot\" ||\n        core.store.state.warmTransition !== undefined ||\n        !recoveryIdentityMatches(\n          core.store.state.identity,\n          completion.identity,","sourceCodeStart":5436,"sourceCodeEnd":5472,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/live/runnerd-codex-transport.ts#L5436-L5472","documentation":"Thrown from the private `#commandResult` helper of the runnerd Codex transport when the lazily-created provider-thread core (`this.#core`) is null. The helper can only queue and await durable PRP commands while the provider thread is started; without it there is nowhere to route `queueCommand`, so the call is rejected immediately.","triggerScenarios":"Invoking any PRP command path (`#commandResult(type, payload, deadline)`) — e.g. session.snapshot or warm-recovery commands — before the transport's start/activation sequence has initialized the core, or after the core was torn down/reset to null during shutdown or failed recovery.","commonSituations":"Calling transport methods out of order (command issued before `start()` completed); a warm-recovery path running after the core was cleared; racing a shutdown against an in-flight command; a failed prior start leaving the transport half-initialized.","solutions":["Ensure the transport's start/activation sequence completed successfully before issuing PRP commands.","Check whether an earlier error or shutdown nulled `#core`; re-create/restart the transport instead of reusing it.","Guard callers so commands are not enqueued concurrently with teardown.","If using warm recovery, verify the checkpoint/provider-thread activation finished before sending session.snapshot commands."],"exampleFix":"// before\nawait transport.#commandResult(\"session.snapshot\", payload);\n// after\nif (!transport.isProviderThreadStarted()) {\n  await transport.start();\n}\nawait transport.#commandResult(\"session.snapshot\", payload);","handlingStrategy":"type-guard","validationCode":"if (!transport.isProviderThreadStarted()) throw new Error(\"start the transport before issuing PRP commands\");","typeGuard":"function canIssuePrpCommand(t) {\n  return typeof t.isProviderThreadStarted === \"function\" && t.isProviderThreadStarted();\n}","tryCatchPattern":"try {\n  await issueCommand();\n} catch (err) {\n  if (err.message.includes(\"provider thread is not started\")) {\n    await transport.start();\n    await issueCommand();\n  } else throw err;\n}","preventionTips":["Always await transport.start() before any command API.","Serialize commands against shutdown with a lifecycle lock.","Treat a failed start as terminal: recreate the transport instead of reusing it."],"tags":["state","lifecycle","runnerd","initialization"],"backgroundTag":"invalid-state-transition","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}