{"record":{"id":"91715a0e431e3136","repo":"pydantic/monty","slug":"montyfilehandle-path-must-be-a-string-value","errorCode":null,"errorMessage":"MontyFileHandle path must be a string","messagePattern":"MontyFileHandle path must be a string","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/worker/value.ts","lineNumber":248,"sourceCode":"    isDataclass: object.isDataclass === true,\n    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. */","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/worker/value.ts#L230-L266","documentation":"pushFileHandle converts a MontyFileHandle marker object into a flat arena node. The handle's virtual `path` (the sandbox-side POSIX path of the open file) must be a string; this TypeError is thrown when it is not — a number, null, or missing property all fail. The path is forwarded verbatim to the sandbox, which resolves it against the mount table.","triggerScenarios":"Passing a file-handle marker whose `path` is undefined because the handle was constructed by hand; passing a host Path object or URL object instead of a plain string; JSON round-trip where a null replaced the path.","commonSituations":"Building file-handle markers from host-side file descriptors or Pathly objects without extracting `.toString()`; config where the path field is optional but the marker requires it; deserializing markers where undefined became null.","solutions":["Pass the path as a plain string: `path: '/mnt/data/file.txt'` — call `.toString()` or template-literal it if it comes from a Path/URL object.","Check the marker is not missing the `path` property entirely (undefined also fails the typeof check).","Validate before the call: `typeof handle.path === 'string' && handle.path.startsWith('/')`.","Remember paths are sandbox-virtual POSIX paths, not host paths — pass the virtual path, not the host absolute path."],"exampleFix":"// before\nval.path = hostDir.resolve('file.txt')  // a Path object\n// after\nval.path = hostDir.resolve('file.txt').toString()  // or the virtual path '/mnt/file.txt'","handlingStrategy":"type-guard","validationCode":"if (typeof handle.path !== 'string' || !handle.path.startsWith('/')) {\n  throw new TypeError(`expected virtual POSIX path string, got ${typeof handle.path}`);\n}","typeGuard":"const hasStringPath = (h: unknown): h is { path: string; [k: string]: unknown } =>\n  typeof h === 'object' && h !== null && typeof (h as any).path === 'string';","tryCatchPattern":"try {\n  await session.feedRun(code, { inputs: { fh: handle } });\n} catch (e) {\n  if (e instanceof TypeError && e.message === 'MontyFileHandle path must be a string') {\n    handle.path = String(handle.path);\n  } else throw e;\n}","preventionTips":["Pass sandbox-virtual POSIX paths ('/mnt/...'), never host Path/URL objects","Call .toString() when the path comes from a filesystem abstraction","Treat path as required — there is no default, undefined throws","Validate marker fields with a guard before each feedRun"],"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-14T11:17:12.474Z"}