Yeachan-Heo/oh-my-codex · error · Error
${name} must be a string
Error message
${name} must be a string What it means
normalizeString in the Hermes bridge throws 'X must be a string' when an argument is present but has a non-string JSON type (number, boolean, object, array). This guards downstream string operations from type confusion in untyped MCP input.
Source
Thrown at src/mcp/hermes-bridge.ts:139
const OMX_INSTANCE_OPTION = "@omx_instance_id";
function jsonResult<T extends Record<string, unknown>>(data: T): HermesBridgeResult<T> {
return { ok: true, data };
}
function failure<T extends Record<string, unknown> = Record<string, unknown>>(
code: HermesBridgeFailureCode,
error: string,
): HermesBridgeResult<T> {
return { ok: false, code, error };
}
function normalizeString(value: unknown, name: string, options: { required?: boolean } = {}): string | undefined {
if (value == null) {
if (options.required) throw new Error(`${name} is required`);
return undefined;
}
if (typeof value !== "string") throw new Error(`${name} must be a string`);
const trimmed = value.trim();
if (!trimmed && options.required) throw new Error(`${name} must be non-empty`);
return trimmed || undefined;
}
function requireMutation(args: Record<string, unknown>): void {
if (args.allow_mutation !== true) {
throw new Error("mutating Hermes bridge tools require allow_mutation: true");
}
}
function normalizePositiveInteger(value: unknown, fallback: number, max: number): number {
if (value == null) return fallback;
const parsed = typeof value === "number" ? value : Number.parseInt(String(value), 10);
if (!Number.isInteger(parsed) || parsed <= 0) return fallback;
return Math.min(parsed, max);
}
View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Convert the value to a string in the client before sending (String(sessionId))
- Align the client's type definitions with the tool schema
- Avoid wrapping scalars in objects/arrays
Example fix
// before
{ session_id: 12345 }
// after
{ session_id: "12345" } Defensive patterns
Strategy: type-guard
Validate before calling
Object.entries(args).forEach(([k,v]) => { if (v != null && typeof v !== 'string') args[k] = String(v); }); Type guard
function isStringArgs(a: Record<string, unknown>): a is Record<string, string | undefined> {
return Object.values(a).every(v => v == null || typeof v === 'string');
} Prevention
- Serialize ids as strings at the source
- Use the tool's generated types in the client
- Never send numbers where ids are documented as strings
When it happens
Trigger: Passing e.g. session_id: 12345, status: true, or cwd: {"path":"/x"} to a Hermes bridge tool.
Common situations: Client sending numbers for ids, booleans for flags expected as strings, or nested objects where a string is expected — often from auto-generated clients mapping types incorrectly.
Understand the failure class
Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.
Related errors
- ${name} is required
- ${name} must be non-empty
- mutation_not_allowed
- unsupported_session_kind
- tmux_instance_mismatch
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/c797688df498b85e.
Report an issue: GitHub.