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

  1. Use exactly one of: running, blocked, failed, complete
  2. Map your internal status enum to these four values before calling
  3. 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

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


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