pydantic/monty · error · TypeError
MontyFileHandle mode must be a string
Error message
MontyFileHandle mode must be a string
What it means
pushFileHandle requires the file-handle marker's `mode` (the file open mode such as 'r', 'w', 'rb') to be a string; this TypeError is thrown for numbers, null, undefined, or any non-string. The mode is further normalized by canonicalFileMode before crossing the wire, but the typeof check happens first.
Source
Thrown at crates/monty-js/ts/worker/value.ts:249
attrs: pushPairs(attrPairs, nodes),
}
}
/** A canonical uuid string is required for identities crossing the wire. */
function uuidString(value: unknown, what: string): string {
if (
typeof value !== 'string' ||
!/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(value)
) {
throw new TypeError(`${what} must be a canonical uuid string`)
}
return value.toLowerCase()
}
/** Validates and converts a sandbox file-handle marker. */
function pushFileHandle(object: Record<string, unknown>): ValueNode {
if (typeof object.path !== 'string') throw new TypeError('MontyFileHandle path must be a string')
if (typeof object.mode !== 'string') throw new TypeError('MontyFileHandle mode must be a string')
const position = object.position === undefined ? 0 : object.position
validateFilePosition(position)
return {
tag: 'file-handle',
val: { path: object.path, mode: canonicalFileMode(object.mode), position: BigInt(position) },
}
}
/** Appends key/value pairs while preserving their insertion order. */
function pushPairs(pairs: [unknown, unknown][], nodes: ValueNode[]): NodePair[] {
return pairs.map(([key, value]) => ({ key: pushValue(key, nodes), value: pushValue(value, nodes) }))
}
/** Fetches a raw arena node by index with the same bounds/cycle checks as
* `readValue`, for callers that must inspect the node's tag. The index stays
* marked as visiting, so a parent cycle in class-type nodes throws instead
* of recursing forever. */
function readValueNode(index: number, nodes: ValueNode[], visiting: Set<number>): ValueNode {View on GitHub (pinned to adc986b362)
Solutions
- Pass the mode as a string: `mode: 'r'`, `mode: 'w'`, `mode: 'rb'`, etc.
- If you have a numeric mode from another API, map it to its string form before constructing the marker.
- Validate before the call: `typeof handle.mode === 'string'`.
- Check for undefined — a missing `mode` property produces this same error, not a default.
Example fix
// before val.mode = 0 // numeric FileMode // after val.mode = 'r'
Defensive patterns
Strategy: type-guard
Validate before calling
const FILE_MODES = new Set(['r', 'w', 'a', 'x', 'r+', 'w+', 'a+', 'x+', 'rb', 'wb', 'ab', 'xb', 'rb+', 'wb+', 'ab+', 'xb+']);
if (typeof handle.mode !== 'string' || !FILE_MODES.has(handle.mode)) {
throw new TypeError(`expected string file mode, got ${handle.mode}`);
} Type guard
const hasStringMode = (h: unknown): h is { mode: string; [k: string]: unknown } =>
typeof h === 'object' && h !== null && typeof (h as any).mode === 'string'; Try / catch
try {
await session.feedRun(code, { inputs: { fh: handle } });
} catch (e) {
if (e instanceof TypeError && e.message === 'MontyFileHandle mode must be a string') {
handle.mode = NUMERIC_MODE_TO_STRING[handle.mode] ?? 'r';
} else throw e;
} Prevention
- Always express file modes as strings ('r', 'w', 'rb'), never numeric enums
- Map numeric modes from other bindings to strings before building markers
- Remember mode is required — omitted properties throw rather than defaulting
- Share one constant list of valid modes between marker construction and validation
When it happens
Trigger: Passing a file-handle marker with `mode: 0`, `mode: null`, or an omitted `mode` property; numeric FileMode enum values from another binding passed directly into the JS API instead of the string form.
Common situations: Mixing bindings (Python/Rust numeric file modes vs JS string modes); constructing markers by hand and forgetting mode; deserialization that turned the mode into null.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- MontyFileHandle path must be a string
- Unknown Monty marker type: ${String(object[TYPE_MARKER])}
- ClassType attrs entries must be [name, value] pairs
- ClassType attr name must be a string
- ClassType attr value missing
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/e66e678cbc00986c.
Report an issue: GitHub.