pydantic/monty · error · TypeError

ClassInstance marker instanceId must be a uuid string

Error message

ClassInstance marker instanceId must be a uuid string

What it means

When restoring an inbound ClassInstance marker, markerToInstance requires the marker's instanceId to be a string (a uuid) so it can look the instance up in the store. A marker whose instanceId is missing or of any other type throws this TypeError — normally a sign of a corrupted or hand-crafted marker.

Source

Thrown at crates/monty-js/ts/classInstance.ts:653

 */
function classTypeObject(wrapper: ClassType, store: InstanceStore, depth: number): Record<string, unknown> {
  const attrs = wrapper
    .getEagerAttrs()
    .map(([name, value]): [string, unknown] => [name, prepareInner(value, store, depth + 1)])
  return {
    name: wrapper.getName(),
    id: wrapper.id,
    hostDefined: true,
    // JS has no dataclasses; host-wrapped objects always cross as plain classes
    isDataclass: false,
    attrs,
  }
}

/** Maps an inbound `ClassInstance` marker to the original instance or a proxy. */
function markerToInstance(marker: Record<string, unknown>, store: InstanceStore): unknown {
  if (typeof marker.instanceId !== 'string') {
    throw new TypeError('ClassInstance marker instanceId must be a uuid string')
  }
  const wrapper = store.get(marker.instanceId)
  if (wrapper !== undefined) {
    return wrapper.instance
  }
  const attrs: Array<[string, unknown]> = []
  if (Array.isArray(marker.attrs)) {
    for (const pair of marker.attrs as unknown[]) {
      if (Array.isArray(pair) && typeof pair[0] === 'string') {
        attrs.push([pair[0], restore(pair[1], store)])
      }
    }
  }
  const classType = (marker.type ?? {}) as Record<string, unknown>
  return new MontyClassProxy(classType, marker.instanceId, attrs)
}

/** Maps an inbound `Type` marker to the registered host class, else leaves

View on GitHub (pinned to adc986b362)

Solutions

  1. Ensure markers always come from the library's own prepare/restore pipeline, not hand-built objects
  2. Validate that serialized snapshots contain a string instanceId before restoring
  3. If a legitimately unknown instanceId is expected, wrap fresh host objects in ClassInstance(...) so a new id is assigned

Example fix

// before
restore({ type: 'ClassInstance', instanceId: 42 });
// after
restore({ type: 'ClassInstance', instanceId: '3f2504e0-4f89-11d3-9a0c-0305e82c3301' });
Defensive patterns

Strategy: validation

Validate before calling

function validateMarker(marker) {
  if (marker?.type === 'ClassInstance' && typeof marker.instanceId !== 'string') {
    throw new Error('corrupt ClassInstance marker: instanceId must be a string uuid');
  }
}

Type guard

function hasValidInstanceId(marker) {
  return typeof marker === 'object' && marker !== null && typeof marker.instanceId === 'string';
}

Try / catch

try {
  const obj = restore(marker);
} catch (e) {
  if (e instanceof TypeError && e.message.includes('instanceId must be a uuid string')) {
    throw new Error('marker data is corrupt or hand-built; recreate it via the library API');
  }
  throw e;
}

Prevention

When it happens

Trigger: A marker-shaped object reaching restore with instanceId undefined, a number, or a non-string value — e.g. restored wire data that was truncated, altered, or built by hand.

Common situations: Manually constructing marker objects instead of using the library's prepare output; partial deserialization dropping the instanceId field; JSON schemas where instanceId collided with another key.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


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