affaan-m/ECC · error · Error
Invalid legacy Codex sync state at
Error message
Invalid legacy Codex sync state at ${statePath} What it means
parseState JSON-parses the legacy Codex sync state file and validates its shape: it must contain schema === SCHEMA and an Array paths. If the file exists but is not valid JSON, was written for a different schema version, or is corrupted/truncated, this error is thrown with the offending path. It guards downstream code that iterates state.paths unconditionally.
Solutions
- Inspect the state file (cat statePath) and fix or restore the expected { schema, paths: [...] } structure.
- Delete the invalid state file and re-run the install so beginLegacySyncState writes a fresh, schema-correct snapshot.
- If you intentionally changed SCHEMA, migrate the on-disk file to the new schema value and ensure paths is an array.
- Reinstall the matching plugin version so the SCHEMA constant matches the state file that was written with it.
Example fix
// before (hand-edited state.json)
{ "version": 2, "files": ["a"] }
// after
{ "schema": "<SCHEMA value from codex-legacy-sync.js>", "paths": [{ "path": "...", "installedSha256": "..." }] }
// or simply: rm state.json && rerun install Defensive patterns
Strategy: validation
Validate before calling
import fs from 'node:fs';
function looksLikeValidState(statePath) {
try {
const s = JSON.parse(fs.readFileSync(statePath, 'utf8'));
return typeof s.schema === 'string' && Array.isArray(s.paths);
} catch {
return false;
}
} Type guard
function isLegacySyncState(value) {
return (
typeof value === 'object' && value !== null &&
'schema' in value && typeof value.schema === 'string' &&
'paths' in value && Array.isArray(value.paths)
);
} Try / catch
let state;
try {
state = readState(statePath);
} catch (err) {
if (err instanceof Error && err.message.startsWith('Invalid legacy Codex sync state')) {
fs.rmSync(statePath, { force: true }); // or archive it
state = beginLegacySyncState({ codexHome }); // regenerate
} else {
throw err;
}
} Prevention
- Never hand-edit the state file; regenerate it via install instead.
- Keep the plugin version constant between writing and reading state, or migrate the schema explicitly.
- Check that writes completed (the atomic temp+rename pattern guards this) — inspect for truncated JSON after crashes.
- Validate the file with a JSON parser before running update/rollback commands.
When it happens
Trigger: Calling readState or readStateIfPresent on a state file whose parsed JSON has state.schema !== SCHEMA or state.paths is not an array — e.g. hand-edited state, truncated write, state produced by an older/newer plugin version with a different SCHEMA constant.
Common situations: Manually editing or minifying the state file; a partial write from a crash before the atomic temp-file rename; upgrading/downgrading the plugin so the embedded SCHEMA constant no longer matches the on-disk file; state file clobbered by another tool writing its own JSON.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid at : expected a JSON object
- Legacy Codex sync state not found at
- Refusing to replace modified legacy Codex artifact
- application request must be a JSON object
- bundle values must be finite JSON data
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/06392d888ee51814.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/codex-legacy-sync.js:152
fs.writeFileSync(tempPath, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
fs.renameSync(tempPath, filePath);
}
function readState(statePath) {
const snapshot = readRegularFileNoFollow(statePath, 'utf8');
if (!snapshot) throw new Error(`Legacy Codex sync state not found at ${statePath}`);
return parseState(snapshot.content, statePath);
}
function readStateIfPresent(statePath) {
const snapshot = readRegularFileNoFollow(statePath, 'utf8');
return snapshot ? parseState(snapshot.content, statePath) : null;
}
function parseState(content, statePath) {
const state = JSON.parse(content);
if (state.schema !== SCHEMA || !Array.isArray(state.paths)) {
throw new Error(`Invalid legacy Codex sync state at ${statePath}`);
}
return state;
}
function hasUnsafeManagedAncestor(filePath, codexHome) {
const relativePath = path.relative(codexHome, filePath);
if (relativePath === '' || relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
return relativePath !== '';
}
const segments = relativePath.split(path.sep).slice(0, -1);
let currentPath = codexHome;
for (const segment of [null, ...segments]) {
if (segment !== null) currentPath = path.join(currentPath, segment);
try {
const stat = fs.lstatSync(currentPath);
if (stat.isSymbolicLink() || !stat.isDirectory()) return true;
} catch (error) {
if (error.code === 'ENOENT') break;View on GitHub (pinned to 8321021c54)