{"record":{"id":"a17c70e1694d0177","repo":"paperclipai/paperclip","slug":"persisted-codex-acpx-resultless-recovery-requires","errorCode":null,"errorMessage":"persisted Codex ACPX resultless recovery requires a completed terminal turn","messagePattern":"persisted Codex ACPX resultless recovery requires a completed terminal turn","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/codex-acpx-driver.ts","lineNumber":2002,"sourceCode":"        )\n      ) {\n        throw new Error(\n          \"persisted Codex ACPX active turn is not the completed semantic settlement\",\n        );\n      }\n    }\n  } else if (terminalTurns.length > 0) {\n    const latestTerminalTurnId = terminalTurns.at(-1)!.turnId;\n    const settlementTurnId = snapshot.activeTurnId ?? latestTerminalTurnId;\n    const settlement = terminalTurns.find(\n      (terminal) => terminal.turnId === settlementTurnId,\n    );\n    if (\n      settlementTurnId !== latestTerminalTurnId ||\n      !settlement ||\n      !isCompletedTerminal(settlement.fingerprint)\n    ) {\n      throw new Error(\n        \"persisted Codex ACPX resultless recovery requires a completed terminal turn\",\n      );\n    }\n  }\n}\n\nfunction validProviderLifetimeFenceCandidates(\n  value: unknown,\n): value is readonly [number, number, number] {\n  return (\n    Array.isArray(value) &&\n    value.length === 3 &&\n    value.every(\n      (port) => Number.isSafeInteger(port) && port >= 49_152 && port <= 65_535,\n    ) &&\n    new Set(value).size === 3\n  );\n}","sourceCodeStart":1984,"sourceCodeEnd":2020,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/codex-acpx-driver.ts#L1984-L2020","documentation":"For a resultless recovery (a Codex ACPX session recovered without a durably settled result), the driver requires that the settlement turn is the session's latest terminal turn and that its fingerprint marks a completed terminal state. This prevents resuming from an incomplete or superseded turn, which could duplicate work or lose results.","triggerScenarios":"Recovering a resultless session where settlementTurnId !== latestTerminalTurnId, the settlement record is missing, or the settlement fingerprint is not a completed terminal (e.g. aborted/failed/in-progress terminal).","commonSituations":"Crash mid-turn leaving only a non-completed terminal turn; a later turn superseding the settled one before recovery; persistence truncated so the settlement record is absent while turn IDs exist.","solutions":["Discard the stale snapshot and start a new session/turn rather than recovering an incomplete one.","Ensure the turn reaches a completed terminal state and its result is settled before the process can be recovered.","Fix persistence flushing so settlement records are written atomically with terminal turn state.","If a newer turn exists, re-run recovery logic against the latest terminal turn instead of the old settlement."],"exampleFix":"// before\nrecoverResultless(snapshot); // settlementTurnId points at superseded turn 7, latest is 9\n// after\nif (settlementTurnId === latestTerminalTurnId && isCompletedTerminal(settlement.fingerprint)) {\n  recoverResultless(snapshot);\n} else {\n  await driver.openSession(); // fresh start\n}","handlingStrategy":"try-catch","validationCode":"function canRecoverResultless(snapshot) {\n  return snapshot.settlementTurnId === snapshot.latestTerminalTurnId &&\n    snapshot.settlement != null && isCompletedTerminal(snapshot.settlement.fingerprint);\n}\nif (!canRecoverResultless(snapshot)) planFreshSession = true;","typeGuard":"function hasCompletedSettlement(s) {\n  return s.settlement != null && isCompletedTerminal(s.settlement.fingerprint) &&\n    s.settlementTurnId === s.latestTerminalTurnId;\n}","tryCatchPattern":"try {\n  recoverResultless(snapshot);\n} catch (err) {\n  if (err.message.includes(\"completed terminal turn\")) {\n    await driver.openSession(); // cannot safely resume\n  } else throw err;\n}","preventionTips":["Flush settlement records atomically with terminal turn state.","Never start a new turn before the previous one settles if recovery matters.","Verify settlementTurnId equals the latest terminal turn before attempting recovery."],"tags":["recovery","persistence","codex","acpx"],"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"}