{"record":{"id":"fcf13dd9cb248c2f","repo":"affaan-m/ECC","slug":"legacy-codex-sync-state-requires-recovery-before-r","errorCode":null,"errorMessage":"Legacy Codex sync state requires recovery before reinstall: ${statePath}","messagePattern":"Legacy Codex sync state requires recovery before reinstall: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/codex-legacy-sync.js","lineNumber":232,"sourceCode":"    }\n    const digest = snapshot\n      ? crypto.createHash('sha256').update(snapshot.content).digest('hex')\n      : null;\n    if (digest !== entry.installedSha256) {\n      throw new Error(`Refusing to replace modified legacy Codex artifact: ${filePath}`);\n    }\n  }\n}\n\nfunction beginLegacySyncState(options) {\n  const codexHome = path.resolve(options.codexHome);\n  const statePath = getStatePath(codexHome);\n  const configPath = path.join(codexHome, 'config.toml');\n  const agentsPath = path.join(codexHome, 'AGENTS.md');\n  const installedHooksPath = options.installedHooksPath ? path.resolve(options.installedHooksPath) : null;\n  const priorState = readStateIfPresent(statePath);\n  if (priorState && priorState.status !== 'installed') {\n    throw new Error(`Legacy Codex sync state requires recovery before reinstall: ${statePath}`);\n  }\n  if (priorState) assertInstalledStateUnmodified(priorState);\n  const trustedRoots = [...new Set([\n    codexHome,\n    ...(Array.isArray(priorState?.trustedRoots) ? priorState.trustedRoots : []),\n    ...(priorState?.installedHooksPath ? [priorState.installedHooksPath] : []),\n    ...(installedHooksPath ? [installedHooksPath] : []),\n  ].map(rootPath => path.resolve(rootPath)))];\n  const state = priorState ? {\n    ...priorState,\n    status: 'applying',\n    updatedAt: new Date().toISOString(),\n    backupDir: options.backupDir ? path.resolve(options.backupDir) : priorState.backupDir,\n    installedHooksPath,\n    trustedRoots,\n    rollbackPreviousHooksPath: options.previousHooksPath || null,\n    rollbackPaths: priorState.paths.map(entry => snapshotLegacyPath(path.resolve(entry.path))),\n    previousInstalledState: priorState,","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/codex-legacy-sync.js#L214-L250","documentation":"beginLegacySyncState refuses to start a new legacy Codex sync when a prior sync state file exists at the state path with a status other than 'installed'. The library tracks sync state on disk so it can roll back safely; a non-installed status means a previous run ended mid-flight (started, failed, or partially rolled back) and must be recovered before any new install touches Codex config.","triggerScenarios":"Calling beginLegacySyncState when readStateIfPresent(statePath) returns a prior state object whose status is not 'installed' — e.g. after a crashed sync, a failed rollback, or a manually edited state file.","commonSituations":"A previous `codex sync` run was interrupted (Ctrl-C, crash, power loss) leaving status 'in-progress' or 'failed'; running reinstall without running the recovery step; a stale state file left behind after an aborted upgrade.","solutions":["Open the state file at the path named in the error (statePath) and inspect its status field","Run the recovery/rollback routine for legacy sync state (the library's recovery path) to restore installed state","Delete or restore the stale state file to a valid 'installed' status only after verifying the Codex home files match expectations","Re-run the sync/reinstall once the state is recovered"],"exampleFix":"// before\n$ ecc codex sync   // throws: requires recovery before reinstall\n// after\n$ ecc codex sync --recover   # or remove/recover the stale state file first\n$ ecc codex sync","handlingStrategy":"validation","validationCode":"const state = readStateIfPresent(statePath);\nif (state && state.status !== 'installed') {\n  runRecovery(statePath); // recover before reinstalling\n}","typeGuard":"function isRecoverable(state) { return state && state.status !== 'installed'; }","tryCatchPattern":"try { beginLegacySyncState(codexHome, options); } catch (e) {\n  if (String(e.message).includes('requires recovery')) runRecovery(getStatePath(codexHome));\n  else throw e;\n}","preventionTips":["Always complete or roll back a sync before starting another","Avoid killing the process mid-sync (use tmux/nohup for long syncs)","Monitor the state file status before reinstalling","Automate recovery in CI wrappers that call sync"],"tags":["sync","state","recovery","codex"],"backgroundTag":"invalid-state-transition","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}