affaan-m/ECC · error · Error

record requires --state and --path

Error message

record requires --state and --path

What it means

The `record` step appends a migrated file path to the sync-state journal created by `begin`, so it needs the state file location (--state) and the affected file path (--path). readFlag() returns null for a missing flag, a missing value, or a value starting with '--'; if either --state or --path yields null, this error is thrown before anything is journaled.

Source

Thrown at scripts/codex/legacy-sync-state.js:36

function main(argv = process.argv.slice(2)) {
  const command = argv[0];
  if (command === 'begin') {
    const codexHome = readFlag(argv, '--codex-home');
    const backupDir = readFlag(argv, '--backup-dir');
    if (!codexHome || !backupDir) throw new Error('begin requires --codex-home and --backup-dir');
    process.stdout.write(`${beginLegacySyncState({
      codexHome,
      backupDir,
      previousHooksPath: readFlag(argv, '--previous-hooks-path') || '',
      installedHooksPath: readFlag(argv, '--installed-hooks-path'),
    })}\n`);
    return;
  }
  if (command === 'record') {
    const statePath = readFlag(argv, '--state');
    const filePath = readFlag(argv, '--path');
    if (!statePath || !filePath) throw new Error('record requires --state and --path');
    recordLegacySyncPath({ statePath, filePath });
    return;
  }
  if (command === 'finalize') {
    const statePath = readFlag(argv, '--state');
    if (!statePath) throw new Error('finalize requires --state');
    finalizeLegacySyncState({ statePath });
    return;
  }
  if (command === 'rollback') {
    const statePath = readFlag(argv, '--state');
    if (!statePath) throw new Error('rollback requires --state');
    const result = rollbackLegacyCodexSync({ statePath });
    process.stdout.write(`${JSON.stringify(result)}\n`);
    if (result.status !== 'rolled-back') process.exitCode = 1;
    return;
  }
  throw new Error('Usage: legacy-sync-state.js <begin|record|finalize|rollback> [options]');

View on GitHub (pinned to 06c5e118c4)

Solutions

  1. Capture begin's output and pass both values: `STATE=$(node scripts/codex/legacy-sync-state.js begin --codex-home ... --backup-dir ...)`, then `record --state "$STATE" --path <file>`
  2. Ensure both values are non-empty and neither starts with '--'
  3. If the state file was lost, restart the sequence with a fresh `begin` before recording

Example fix

# before
node scripts/codex/legacy-sync-state.js record --path hooks.json
# after
STATE=$(node scripts/codex/legacy-sync-state.js begin --codex-home ~/.codex --backup-dir /tmp/bak)
node scripts/codex/legacy-sync-state.js record --state "$STATE" --path hooks.json
Defensive patterns

Strategy: validation

Validate before calling

function readFlag(args, name) {
  const i = args.indexOf(name);
  if (i === -1) return null;
  const v = args[i + 1];
  return v && !v.startsWith('--') ? v : null;
}
if (!readFlag(argv, '--state') || !readFlag(argv, '--path')) {
  throw new Error('record requires --state and --path');
}

Type guard

function hasRequiredRecordFlags(argv) {
  const get = n => { const i = argv.indexOf(n); const v = argv[i + 1]; return i !== -1 && v && !v.startsWith('--'); };
  return get('--state') && get('--path');
}

Try / catch

try {
  main(argv);
} catch (error) {
  if (/^record requires/.test(error.message)) {
    process.stderr.write('Usage: legacy-sync-state.js record --state <file> --path <file>\n');
  }
}

Prevention

When it happens

Trigger: `record` invoked without --state or without --path; the state path from `begin` not captured (begin prints it to stdout, which wrappers must thread through); a value slot occupied by the next flag.

Common situations: Wrapper scripts that call begin but forget to capture its printed state path before calling record; manual invocation while debugging the sync; empty variables interpolated as values.

Related errors


AI-assisted analysis of affaan-m/ECC@06c5e118c4 (2026-08-18). Data as JSON: /api/errors/3bf5314970c54df7. Report an issue: GitHub.