affaan-m/ECC · error · Error
Legacy Codex sync state requires recovery before reinstall
Error message
Legacy Codex sync state requires recovery before reinstall: ${statePath} What it means
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.
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
Example fix
// before $ ecc codex sync // throws: requires recovery before reinstall // after $ ecc codex sync --recover # or remove/recover the stale state file first $ ecc codex sync
Defensive patterns
Strategy: validation
Validate before calling
const state = readStateIfPresent(statePath);
if (state && state.status !== 'installed') {
runRecovery(statePath); // recover before reinstalling
} Type guard
function isRecoverable(state) { return state && state.status !== 'installed'; } Try / catch
try { beginLegacySyncState(codexHome, options); } catch (e) {
if (String(e.message).includes('requires recovery')) runRecovery(getStatePath(codexHome));
else throw e;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- a top-level committed transaction is required
- Codex probe failed ` : ''}
- Codex rollout session not found
- Codex version could not be verified for tool-less review
- Codex cannot guarantee tool-less review; required stable…
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/fcf13dd9cb248c2f.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/codex-legacy-sync.js:232
}
const digest = snapshot
? crypto.createHash('sha256').update(snapshot.content).digest('hex')
: null;
if (digest !== entry.installedSha256) {
throw new Error(`Refusing to replace modified legacy Codex artifact: ${filePath}`);
}
}
}
function beginLegacySyncState(options) {
const codexHome = path.resolve(options.codexHome);
const statePath = getStatePath(codexHome);
const configPath = path.join(codexHome, 'config.toml');
const agentsPath = path.join(codexHome, 'AGENTS.md');
const installedHooksPath = options.installedHooksPath ? path.resolve(options.installedHooksPath) : null;
const priorState = readStateIfPresent(statePath);
if (priorState && priorState.status !== 'installed') {
throw new Error(`Legacy Codex sync state requires recovery before reinstall: ${statePath}`);
}
if (priorState) assertInstalledStateUnmodified(priorState);
const trustedRoots = [...new Set([
codexHome,
...(Array.isArray(priorState?.trustedRoots) ? priorState.trustedRoots : []),
...(priorState?.installedHooksPath ? [priorState.installedHooksPath] : []),
...(installedHooksPath ? [installedHooksPath] : []),
].map(rootPath => path.resolve(rootPath)))];
const state = priorState ? {
...priorState,
status: 'applying',
updatedAt: new Date().toISOString(),
backupDir: options.backupDir ? path.resolve(options.backupDir) : priorState.backupDir,
installedHooksPath,
trustedRoots,
rollbackPreviousHooksPath: options.previousHooksPath || null,
rollbackPaths: priorState.paths.map(entry => snapshotLegacyPath(path.resolve(entry.path))),
previousInstalledState: priorState,View on GitHub (pinned to 8321021c54)