pydantic/monty · error · TypeError
${what} must be a canonical uuid string
Error message
${what} must be a canonical uuid string What it means
Identities (class instances, class types) crossing the wasm boundary must be canonically-formatted UUID strings (8-4-4-4-12 hex digits, hyphen-separated). uuidString validates the `id` field of such markers and throws this TypeError with the given label (`what`) when the value is missing, not a string, or not matching the UUID regex. It then lowercases the uuid for the wire.
Source
Thrown at crates/monty-js/ts/worker/value.ts:241
if (!(1 in pair)) throw new TypeError('ClassType attr value missing')
attrPairs.push([pair[0], pair[1]])
}
return {
name: String(object.name),
id: uuidString(object.id, 'ClassType id'),
hostDefined: object.hostDefined === true,
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[] {View on GitHub (pinned to adc986b362)
Solutions
- Generate ids with `crypto.randomUUID()` which always produces the canonical hyphenated form.
- Normalize existing ids: strip `urn:uuid:` prefix and braces, insert hyphens, e.g. via a regex rewrite before validation.
- Check for undefined/null ids — the property may simply be missing on the marker object.
- Validate before the call: `/^[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(id)`.
Example fix
// before
const marker = { [TYPE_MARKER]: 'ClassType', name: 'Foo', id: Math.random().toString(36) }
// after
const marker = { [TYPE_MARKER]: 'ClassType', name: 'Foo', id: crypto.randomUUID() } Defensive patterns
Strategy: validation
Validate before calling
const UUID_RE = /^[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}$/;
if (typeof marker.id !== 'string' || !UUID_RE.test(marker.id)) {
marker.id = crypto.randomUUID();
} Type guard
const isUuid = (v: unknown): v is string =>
typeof v === '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(v); Try / catch
try {
await session.feedRun(code, { inputs: { inst: marker } });
} catch (e) {
if (e instanceof TypeError && e.message.endsWith('must be a canonical uuid string')) {
marker.id = normalizeToUuid(marker.id); // strip urn:/braces, insert hyphens, or regenerate
} else throw e;
} Prevention
- Always generate identities with crypto.randomUUID()
- Strip urn:uuid: prefixes and braces when accepting ids from external systems
- Reuse one stable uuid per logical class/instance across calls instead of regenerating
- Add an isUuid assertion at the boundary where markers are created
When it happens
Trigger: Passing a ClassInstance/ClassType marker with `id` set to a plain string like `'abc123'`, a number, a NaN-generated id, or an id with braces/urn prefix like `'{...}'` or `'urn:uuid:...'`. Also when `id` is undefined because it was never assigned.
Common situations: Using `Math.random()`-based ids instead of real UUIDs (e.g. crypto.randomUUID()); passing UUIDs in non-canonical formats (braces, no hyphens, uppercase-only frameworks); forgetting to persist and reuse a stable id across calls.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- 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
- MontyFileHandle path must be a string
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/e17d6f45b1a58940.
Report an issue: GitHub.