pydantic/monty · error · TypeError

raw ClassInstance markers are not accepted — wrap the object

Error message

raw ClassInstance markers are not accepted — wrap the object in ClassInstance(...)

What it means

Identity-bearing ClassInstance wire markers ({ type: 'ClassInstance', instanceId: ... }) are produced only by the internal prepare walk; host code must never hold one. One arriving at prepareInner is treated as forged — e.g. embedded in attacker-controlled JSON to impersonate a registered instance — and rejected with this TypeError. Wrap real host objects in the ClassInstance(...) function instead.

Source

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

  if (Array.isArray(value)) {
    return walkArray(value, walk)
  }
  if (value instanceof Map) {
    return walkMap(value, walk)
  }
  if (value instanceof Set) {
    return walkSet(value, walk)
  }
  if (value instanceof Uint8Array) {
    return value
  }
  const marker = readTypeMarker(value)
  if (marker === 'ClassInstance') {
    // Identity-bearing markers are produced internally by this walk, never
    // held by host code (`restore` maps them to the original object or a
    // MontyClassProxy). One arriving here is forged — e.g. embedded
    // in attacker-controlled JSON to impersonate a registered instance.
    throw new TypeError('raw ClassInstance markers are not accepted — wrap the object in ClassInstance(...)')
  }
  if (marker === 'Type' && (value as { classType?: unknown }).classType !== undefined) {
    // Same reasoning for a host-class marker; builtin `Type` markers
    // (`{ value: 'int' }`) carry no identity and pass through.
    throw new TypeError('raw Type markers are not accepted — pass the class through ClassType(...)')
  }
  if (marker !== undefined) {
    return value
  }
  if (isPlainObject(value)) {
    return walkPlainObject(value as Record<string, unknown>, walk)
  }
  throw new TypeError(
    `Cannot convert ${constructorName(value)} instance to a Monty value — wrap it in ClassInstance(...)`,
  )
}

/**

View on GitHub (pinned to adc986b362)

Solutions

  1. Wrap the original host object with ClassInstance(obj, ClassType(MyClass)) instead of passing the marker
  2. If you serialized inputs earlier, persist the original object (or a host-side reference) rather than the marker object
  3. Strip or re-map marker-shaped fields from untrusted JSON before passing it as sandbox input

Example fix

// before
session.feedRun('use(obj)', { inputs: { obj: { type: 'ClassInstance', instanceId: '...' } } });
// after
session.feedRun('use(obj)', { inputs: { obj: ClassInstance(myObject, MyClassType) } });
Defensive patterns

Strategy: type-guard

Validate before calling

function looksLikeClassInstanceMarker(v) {
  return typeof v === 'object' && v !== null &&
    (v.type === 'ClassInstance' || ('instanceId' in v && !('__montyWrapper' in v)));
}
if (looksLikeClassInstanceMarker(inputs.obj)) throw new Error('pass the original object wrapped in ClassInstance(...)');

Type guard

function isWrappedInstance(v) {
  return typeof v === 'object' && v !== null && typeof v.__montyWrapper !== 'undefined';
}

Try / catch

try {
  await session.feedRun(code, { inputs });
} catch (e) {
  if (e instanceof TypeError && e.message.includes('raw ClassInstance markers are not accepted')) {
    throw new Error('caller passed a wire marker; wrap the original object with ClassInstance(obj, ClassType(Cls))');
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing a raw marker object (an object with a type/ClassInstance marker, typically round-tripped from a previous restore or parsed from untrusted JSON) as an input, external-function result, or ClassInstance attribute instead of the actual host object wrapped in ClassInstance(...).

Common situations: Caching the wire-format output of a previous call and feeding it back in; storing 'restore' output in a database and re-sending it; accepting host objects from untrusted JSON that happens to contain marker-shaped keys.

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


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