Yeachan-Heo/oh-my-codex · error · Error

Autopilot handoff workingDirectory does not match the select

Error message

Autopilot handoff workingDirectory does not match the selected workspace.

What it means

Thrown by assertBoundHandoffIdentity() when the handoff JSON contains a workingDirectory string that differs from the current working directory (workspace) of the autopilot command. It prevents applying a handoff bound to one workspace to another workspace.

Source

Thrown at src/cli/autopilot.ts:60

    if (valueFlags.has(args[i])) { i += 1; continue; }
    if (!args[i].startsWith('--')) words.push(args[i]);
  }
  return words.join(' ').trim();
}

async function jsonInput(raw: string): Promise<Record<string, unknown>> {
  const text = raw.trim().startsWith('{') ? raw : await readFile(raw, 'utf-8');
  const parsed = JSON.parse(text) as unknown;
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('--handoff-json must resolve to a JSON object.');
  return parsed as Record<string, unknown>;
}

function assertBoundHandoffIdentity(handoff: Record<string, unknown>, cwd: string, sessionId?: string): void {
  if (typeof handoff.session_id === 'string' && handoff.session_id !== sessionId) {
    throw new Error('Autopilot handoff session_id does not match the selected session.');
  }
  if (typeof handoff.workingDirectory === 'string' && handoff.workingDirectory !== cwd) {
    throw new Error('Autopilot handoff workingDirectory does not match the selected workspace.');
  }
  handoff.session_id = sessionId;
  handoff.workingDirectory = cwd;
}

async function readAutopilot(cwd: string, sessionId?: string) {
  return sessionId
    ? readModeStateForExplicitSession('autopilot', sessionId, cwd)
    : readModeState('autopilot', cwd);
}

/**
 * A terminalization that carried skipped gates must never read as clean success. The durable
 * marker is the machine token `complete-with-skipped-gates`; this renders the human string and
 * names each skipped gate with its missing evidence so the report is actionable.
 */
function skippedGateReport(state: Record<string, unknown>): string | null {
  if (state.completion_status !== 'complete-with-skipped-gates') return null;

View on GitHub (pinned to 3ad79a8a6f)

Solutions

  1. cd into the exact working directory recorded in the handoff's workingDirectory field before running advance
  2. Remove the workingDirectory field (or make it non-string) from the handoff so the current cwd is stamped in
  3. Normalize both paths (resolve symlinks) if they point to the same directory but differ textually

Example fix

// before
cd /other/dir && omx autopilot advance --handoff-json h.json  // h.json has "workingDirectory": "/repo"
// after
cd /repo && omx autopilot advance --handoff-json h.json
Defensive patterns

Strategy: validation

Validate before calling

const h = JSON.parse(fs.readFileSync(handoffPath,'utf-8'));
if (h.workingDirectory && path.resolve(h.workingDirectory) !== path.resolve(process.cwd())) {
  process.chdir(path.resolve(h.workingDirectory));
}

Try / catch

try { await advance(handoff); } catch (e) { if (e.message.includes('workingDirectory does not match')) { process.chdir(recordedDir); await advance(handoff); } else throw e; }

Prevention

When it happens

Trigger: Calling autopilot advance from a directory different from the one where the handoff was produced, while the handoff JSON has a string workingDirectory field. Non-string/absent workingDirectory passes and is overwritten with cwd.

Common situations: Running the CLI from a symlinked or differently-spelled path, moving the repo directory between producing and consuming the handoff, or running the command from the repo root while the handoff was recorded in a subdirectory.

Related errors


AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27). Data as JSON: /api/errors/d38b18806cfd14c9. Report an issue: GitHub.