{"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/06c5e118c4d3e6c3b7f9445f973a2194c82de193/scripts/lib/codex-legacy-sync.js#L214-L250","documentation":"beginLegacySyncState() refuses to reinstall when the prior state's status is anything other than 'installed'. The sync flips status to 'applying' while work is in flight, so a non-installed status means a previous apply or rollback was interrupted and the recorded rollbackPaths may be half-applied; the operator must complete recovery before ECC touches the tree again.","triggerScenarios":"A prior sync crashed or was Ctrl-C'd between writing status 'applying' and completing; an earlier rollback itself failed leaving a non-installed status; the next beginLegacySyncState() call throws immediately on readStateIfPresent().","commonSituations":"CI timeout killing the install mid-run; terminal closed or machine slept during apply; a rollback that hit a filesystem error and aborted.","solutions":["Run the legacy Codex sync rollback/recovery entry point (rollbackLegacyCodexSync restores rollbackPaths and clears the state) before reinstalling","If recovery is unavailable, manually verify managed files are intact, back up and delete the state file, then reinstall fresh","Re-run the original sync to completion if it stopped for a retryable reason (lock, transient error)"],"exampleFix":"// before: beginLegacySyncState throws Legacy Codex sync state requires recovery before reinstall\nrollbackLegacyCodexSync({ statePath }); // finishes the interrupted work\nbeginLegacySyncState({ codexHome }); // now proceeds","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfunction stateNeedsRecovery(statePath) {\n  try {\n    const prior = JSON.parse(fs.readFileSync(statePath, 'utf8'));\n    return typeof prior.status === 'string' && prior.status !== 'installed';\n  } catch { return false; }\n}\nif (stateNeedsRecovery(statePath)) runRecoveryFirst();","typeGuard":"const needsRecovery = (s) => s !== null && typeof s === 'object' && s.status !== undefined && s.status !== 'installed';","tryCatchPattern":"try { beginLegacySyncState({ codexHome }); }\ncatch (err) {\n  if (/requires recovery before reinstall/.test(err.message)) {\n    rollbackLegacyCodexSync({ statePath }).then(() => beginLegacySyncState({ codexHome }));\n  } else throw err;\n}","preventionTips":["Let syncs finish; avoid Ctrl-C during apply","Set CI timeouts above install duration","Run the explicit rollback entry point when aborting a sync"],"tags":["codex","recovery","state-machine","install"],"backgroundTag":"interrupted-state-recovery","analyzedSha":"06c5e118c4d3e6c3b7f9445f973a2194c82de193","analyzedAt":"2026-08-18T11:27:13.915Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}