pydantic/monty · error · TypeError

MontyFileHandle path must be a string

Error message

MontyFileHandle path must be a string

What it means

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.

Source

Thrown at crates/monty-js/ts/worker/value.ts:248

    isDataclass: object.isDataclass === true,
    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. */

View on GitHub (pinned to adc986b362)

Solutions

  1. 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.
  2. Check the marker is not missing the `path` property entirely (undefined also fails the typeof check).
  3. Validate before the call: `typeof handle.path === 'string' && handle.path.startsWith('/')`.
  4. Remember paths are sandbox-virtual POSIX paths, not host paths — pass the virtual path, not the host absolute path.

Example fix

// before
val.path = hostDir.resolve('file.txt')  // a Path object
// after
val.path = hostDir.resolve('file.txt').toString()  // or the virtual path '/mnt/file.txt'
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof handle.path !== 'string' || !handle.path.startsWith('/')) {
  throw new TypeError(`expected virtual POSIX path string, got ${typeof handle.path}`);
}

Type guard

const hasStringPath = (h: unknown): h is { path: string; [k: string]: unknown } =>
  typeof h === 'object' && h !== null && typeof (h as any).path === 'string';

Try / catch

try {
  await session.feedRun(code, { inputs: { fh: handle } });
} catch (e) {
  if (e instanceof TypeError && e.message === 'MontyFileHandle path must be a string') {
    handle.path = String(handle.path);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

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


AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13). Data as JSON: /api/errors/91715a0e431e3136. Report an issue: GitHub.