Yeachan-Heo/oh-my-codex · error · Error
status must be one of running, blocked, failed, complete
Error message
status must be one of running, blocked, failed, complete
What it means
Thrown by the session-report mutation handler when args.status is not one of the four allowed lifecycle values: running, blocked, failed, complete. Status is required and strictly enumerated so downstream state stays consistent.
Source
Thrown at src/mcp/hermes-bridge.ts:654
} finally {
await handle.close();
}
} catch (error) {
return failure("invalid_input", error instanceof Error ? error.message : String(error));
}
}
export async function hermesReportStatus(
args: Record<string, unknown>,
deps: HermesBridgeDeps = {},
): Promise<HermesBridgeResult<{ path: string; report: Record<string, unknown> }>> {
try {
requireMutation(args);
const cwd = resolveWorkingDirectoryForState(normalizeString(args.workingDirectory, "workingDirectory"));
const sessionId = validateSessionId(normalizeString(args.session_id, "session_id"));
const status = normalizeString(args.status, "status", { required: true })!;
if (!["running", "blocked", "failed", "complete"].includes(status)) {
throw new Error("status must be one of running, blocked, failed, complete");
}
const summary = normalizeString(args.summary, "summary");
const prUrl = normalizeString(args.pr_url, "pr_url");
const blocker = normalizeString(args.blocker, "blocker");
const report = {
status,
updated_at: (deps.now ?? (() => new Date()))().toISOString(),
...(summary ? { summary } : {}),
...(prUrl ? { pr_url: prUrl } : {}),
...(blocker ? { blocker } : {}),
};
const stateDir = sessionId ? join(getBaseStateDir(cwd), "sessions", sessionId) : getBaseStateDir(cwd);
const path = join(stateDir, "hermes-coordination.json");
await mkdir(dirname(path), { recursive: true });
await writeFile(path, JSON.stringify(report, null, 2) + "\n");
return jsonResult({ path, report });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Use exactly one of: running, blocked, failed, complete
- Map your internal status enum to these four values before calling
- Validate status against the list before invoking the tool
Example fix
// before
await report({ status: "done", summary: "..." });
// after
await report({ status: "complete", summary: "..." }); Defensive patterns
Strategy: validation
Validate before calling
const STATUSES = ['running', 'blocked', 'failed', 'complete'] as const;
if (!STATUSES.includes(status)) throw new TypeError(`status must be one of ${STATUSES.join(', ')}`); Type guard
type Status = typeof STATUSES[number];
function isStatus(v: unknown): v is Status { return typeof v === 'string' && (STATUSES as readonly string[]).includes(v); } Prevention
- Model status as a closed union type in your client
- Map internal enums to the four allowed words explicitly
When it happens
Trigger: Calling the report tool with status="done", status="success", status="completed" (instead of complete), or an empty string.
Common situations: LLM-generated tool calls inventing synonyms; clients copying status vocabularies from other systems (succeeded/canceled); locale differences.
Related errors
- Invalid setup MCP mode: ${next}. Expected one of: none, comp
- status must be one of open, pending, prompting, answered, ab
- autoresearch candidate artifact status must be candidate|noo
- Invalid setup install mode: ${next}. Expected one of: legacy
- Conflicting setup MCP mode flags: ${source} selects ${next},
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/52bd2c8ebfcf83d5.
Report an issue: GitHub.