{"record":{"id":"2d61631573997a0d","repo":"paperclipai/paperclip","slug":"codex-run-attach-invalid","errorCode":"codex_run_attach_invalid","errorMessage":"codex_run_attach_invalid","messagePattern":"codex_run_attach_invalid","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts","lineNumber":93,"sourceCode":"  ids(): ReturnType<HarnessSession[\"ids\"]> {\n    return {\n      driverSessionId: this.opened.threadId,\n      providerSessionId: this.opened.providerSessionId,\n      displayId: this.opened.threadId,\n    };\n  }\n\n  async attachRun(input: { runId: string }): Promise<void> {\n    this.assertProtocolIntegrity();\n    const transportOwnsQuiescence = this.transport.attachRun !== undefined;\n    if (\n      this.turnStartPending ||\n      (!transportOwnsQuiescence &&\n        (this.activeTurnId !== null || this.pendingRuntimeRequestMap.size > 0))\n    ) {\n      throw new Error(\"codex_run_attach_busy\");\n    }\n    if (!input.runId) throw new Error(\"codex_run_attach_invalid\");\n    await this.transport.attachRun?.({\n      runId: input.runId,\n      turnId: `turn_attachment_${randomUUID().replaceAll(\"-\", \"\")}`,\n      itemId: `item_attachment_${randomUUID().replaceAll(\"-\", \"\")}`,\n    });\n    this.assertProtocolIntegrity();\n    if (transportOwnsQuiescence) {\n      // Runnerd's attachment contract performs two durable readiness probes,\n      // drains the settled provider tail, and rotates authority atomically.\n      // Its proof supersedes host reducer state that can remain stale when a\n      // semantic-result consumer stops before the interrupt terminal arrives.\n      // Drop only the prior run's already-proven-settled buffered suffix.\n      this.activeTurnId = null;\n      this.pendingRuntimeRequestMap.clear();\n      this.eventQueue.clear();\n    }\n    if (this.codexUsageBaseline && input.runId !== this.runId) {\n      this.codexUsageBaseline = { baseline: { ...this.codexUsageBaseline.latest }, latest: { ...this.codexUsageBaseline.latest } };","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/codex/codex-harness-session.ts#L75-L111","documentation":"attachRun validates its input after the busy check and throws \"codex_run_attach_invalid\" when input.runId is falsy (empty string, undefined, or null). A run attachment without a run identifier cannot be recorded or matched against usage baselines, so the session rejects it immediately.","triggerScenarios":"Calling session.attachRun({ runId: \"\" }), attachRun({}), or attachRun(undefined as any) — typically when the caller's run record failed to persist and its id came back empty/undefined, or when a variable holding the run id was never initialized.","commonSituations":"A run-creation API call that returned an error but whose result was destructured anyway; passing a run object's optional id field directly; a regression in orchestration code that lost the runId between run creation and session attach.","solutions":["Ensure the run was successfully created and capture its non-empty id before calling attachRun; log the run-creation response to find where the id was lost.","Guard the call site: throw/return early when runId is missing instead of passing it to attachRun.","If runId comes from persistence, verify the stored run row exists and its id column is populated."],"exampleFix":"// before\nawait session.attachRun({ runId: run.id });\n// after\nif (!run.id) throw new Error(\"run id missing before attach\");\nawait session.attachRun({ runId: run.id });","handlingStrategy":"validation","validationCode":"function canAttach(run) {\n  return typeof run?.id === \"string\" && run.id.length > 0;\n}\nif (!canAttach(run)) throw new Error(\"run must have a non-empty id before attach\");","typeGuard":"function hasRunId(run: unknown): run is { runId: string } {\n  return typeof (run as { runId?: unknown })?.runId === \"string\" &&\n    (run as { runId: string }).runId.length > 0;\n}","tryCatchPattern":"try {\n  await session.attachRun({ runId });\n} catch (e) {\n  if (e.message === \"codex_run_attach_invalid\") {\n    // runId was lost; re-create the run and capture a real id, then retry\n    const run = await createRunRecord();\n    await session.attachRun({ runId: run.id });\n  } else throw e;\n}","preventionTips":["Check the run-creation API result for errors before destructuring the id.","Make runId a required, non-optional type at the call site so TypeScript catches missing ids.","Assert run creation succeeded (non-empty id) in orchestration tests."],"tags":["validation","codex","attach","argument"],"backgroundTag":"missing-required-argument","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"}