{"record":{"id":"e66e678cbc00986c","repo":"pydantic/monty","slug":"montyfilehandle-mode-must-be-a-string-value","errorCode":null,"errorMessage":"MontyFileHandle mode must be a string","messagePattern":"MontyFileHandle mode must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/worker/value.ts","lineNumber":249,"sourceCode":"    attrs: pushPairs(attrPairs, nodes),\n  }\n}\n\n/** A canonical uuid string is required for identities crossing the wire. */\nfunction uuidString(value: unknown, what: string): string {\n  if (\n    typeof value !== 'string' ||\n    !/^[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)\n  ) {\n    throw new TypeError(`${what} must be a canonical uuid string`)\n  }\n  return value.toLowerCase()\n}\n\n/** Validates and converts a sandbox file-handle marker. */\nfunction pushFileHandle(object: Record<string, unknown>): ValueNode {\n  if (typeof object.path !== 'string') throw new TypeError('MontyFileHandle path must be a string')\n  if (typeof object.mode !== 'string') throw new TypeError('MontyFileHandle mode must be a string')\n  const position = object.position === undefined ? 0 : object.position\n  validateFilePosition(position)\n  return {\n    tag: 'file-handle',\n    val: { path: object.path, mode: canonicalFileMode(object.mode), position: BigInt(position) },\n  }\n}\n\n/** Appends key/value pairs while preserving their insertion order. */\nfunction pushPairs(pairs: [unknown, unknown][], nodes: ValueNode[]): NodePair[] {\n  return pairs.map(([key, value]) => ({ key: pushValue(key, nodes), value: pushValue(value, nodes) }))\n}\n\n/** Fetches a raw arena node by index with the same bounds/cycle checks as\n *  `readValue`, for callers that must inspect the node's tag. The index stays\n *  marked as visiting, so a parent cycle in class-type nodes throws instead\n *  of recursing forever. */\nfunction readValueNode(index: number, nodes: ValueNode[], visiting: Set<number>): ValueNode {","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/worker/value.ts#L231-L267","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nval.mode = 0  // numeric FileMode\n// after\nval.mode = 'r'","handlingStrategy":"type-guard","validationCode":"const FILE_MODES = new Set(['r', 'w', 'a', 'x', 'r+', 'w+', 'a+', 'x+', 'rb', 'wb', 'ab', 'xb', 'rb+', 'wb+', 'ab+', 'xb+']);\nif (typeof handle.mode !== 'string' || !FILE_MODES.has(handle.mode)) {\n  throw new TypeError(`expected string file mode, got ${handle.mode}`);\n}","typeGuard":"const hasStringMode = (h: unknown): h is { mode: string; [k: string]: unknown } =>\n  typeof h === 'object' && h !== null && typeof (h as any).mode === 'string';","tryCatchPattern":"try {\n  await session.feedRun(code, { inputs: { fh: handle } });\n} catch (e) {\n  if (e instanceof TypeError && e.message === 'MontyFileHandle mode must be a string') {\n    handle.mode = NUMERIC_MODE_TO_STRING[handle.mode] ?? 'r';\n  } else throw e;\n}","preventionTips":["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"],"tags":["typescript","wasm","filesystem","value-conversion"],"backgroundTag":"type-mismatch","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}