Yeachan-Heo/oh-my-codex · error · Error
shutdown_config_missing:${config.name}
Error message
shutdown_config_missing:${config.name} What it means
mutateShutdownConfig re-reads the team config from disk inside the barrier before mutating it; if readTeamConfig returns null the team's config.json no longer exists (or is unreadable/invalid), so there is nothing to mutate and the shutdown sequence aborts. The name suffix identifies which team was being mutated.
Source
Thrown at src/team/runtime.ts:345
teamName: string,
cwd: string,
leaderSessionId?: string,
): Promise<void> {
await syncExactTeamModeStateOnShutdown(teamName, cwd);
const normalizedLeaderSessionId = typeof leaderSessionId === 'string' ? leaderSessionId.trim() : '';
if (normalizedLeaderSessionId) {
await syncExactTeamModeStateOnShutdown(teamName, cwd, normalizedLeaderSessionId);
}
}
async function mutateShutdownConfig(
config: TeamConfig,
cwd: string,
mutate: (current: TeamConfig) => void,
): Promise<TeamConfig> {
return await withTeamTaskMembershipBarrier(config.name, cwd, async () => {
const current = await readTeamConfig(config.name, cwd);
if (!current) throw new Error(`shutdown_config_missing:${config.name}`);
const baseGeneration = current.config_generation ?? 0;
const teamRoot = join(current.team_state_root ?? resolveCanonicalTeamStateRoot(cwd), 'team', current.name);
const [currentConfigBytes, currentManifestBytes] = await Promise.all([
readFile(join(teamRoot, 'config.json'), 'utf8'),
existsSync(join(teamRoot, 'manifest.v2.json')) ? readFile(join(teamRoot, 'manifest.v2.json'), 'utf8') : null,
]);
mutate(current);
const currentManifest = currentManifestBytes === null ? null : JSON.parse(currentManifestBytes) as Record<string, unknown>;
await commitTeamMembershipTaskTransaction(current.name, cwd, {
baseGeneration,
tasks: [],
config: { oldBytes: currentConfigBytes, newBytes: JSON.stringify(current, null, 2) },
manifest: {
oldBytes: currentManifestBytes,
newBytes: currentManifest === null ? null : JSON.stringify({
...currentManifest,
hud_pane_id: current.hud_pane_id,
hud_pane_pid: current.hud_pane_pid,View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Run `omx team status <name>` to confirm the team state exists in the expected state root
- If state was intentionally removed, treat this as already-shutdown and ignore the error
- Ensure the cwd passed matches the one the team was started in so the canonical state root resolves identically
Defensive patterns
Strategy: try-catch
Validate before calling
const exists = await readTeamConfig(name, cwd);
if (!exists) { /* treat as already shut down; skip mutation */ } Try / catch
try { await shutdownTeam(config, cwd); } catch (e) { if (e instanceof Error && e.message.startsWith('shutdown_config_missing:') && !await readTeamConfig(config.name, cwd)) { return; /* already gone */ } throw e; } Prevention
- Check team status before shutdown
- Always pass the same cwd used at start
- Avoid manually deleting state directories mid-operation
When it happens
Trigger: Calling shutdownTeam or any path through mutateShutdownConfig after the team state directory was deleted, moved, or its config.json corrupted.
Common situations: Concurrent shutdowns racing to delete state, manual rm of the team state root, an external cleanup job, or cwd mismatch resolving a different state root.
Related errors
- shutdown_config_missing_after_commit:${current.name}
- shutdown_shared_session_${kind.replaceAll(' ', '_')}_owner_c
- autoresearch_active_run_exists
- timed out waiting for tmux extended-keys lease lock: ${lockP
- [native-assets] publication-lock-timeout: ${path}; elapsed=$
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/5e048a1706f939ab.
Report an issue: GitHub.