pydantic/monty · error · TypeError

ClassInstance expects an instance of a class, not a null-pro

Error message

ClassInstance expects an instance of a class, not a null-prototype object

What it means

A `TypeError` from the `ClassInstance` constructor: the value is a valid object, but its prototype chain has no constructor — i.e. it is a null-prototype object (e.g. built with `Object.create(null)`) — so `ClassInstance` cannot derive the sandbox-visible class (`classOf(instance)` returns undefined). The sandbox models every wrapped value as an instance of a named class, so a classless object cannot be represented as a `ClassInstance`.

Source

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

 */
export class ClassInstance extends BaseWrapper {
  /** The instance's sandbox identity: reuse one wrapper to re-send an object
   *  under the same id; reusing an id for a different object throws
   *  `TypeError`. */
  readonly id: string
  /** The [`ClassType`] wrapper carrying the class's identity and policies:
   *  `options.classType` if given, else a default one materialized from the
   *  constructor. */
  readonly classType: ClassType

  declare readonly options: ClassInstanceOptions

  constructor(instance: object, options: ClassInstanceOptions = {}) {
    super(instance, options)
    this.id = options.id === undefined ? generateUuid() : normalizeId('ClassInstance', options.id)
    const ctor = classOf(instance)
    if (ctor === undefined) {
      throw new TypeError('ClassInstance expects an instance of a class, not a null-prototype object')
    }
    if (options.classType !== undefined) {
      if (options.classType.classType !== ctor) {
        throw new TypeError("classType does not match the instance's class")
      }
      if (options.name !== undefined) {
        throw new TypeError('pass name on the ClassType wrapper, not alongside classType')
      }
      this.classType = options.classType
    } else {
      this.classType = new ClassType(ctor as new (...args: never[]) => object, { name: options.name })
    }
  }

  /** Class name shown to the sandbox: the class wrapper's, so the instance,
   *  its type object and error messages all agree. */
  override getName(): string {
    return this.classType.getName()

View on GitHub (pinned to adc986b362)

Solutions

  1. If it is just data, pass the plain record directly as a session input — plain objects cross without a wrapper
  2. Give the object a real prototype/class: build it from a class or add `Object.setPrototypeOf(obj, SomeClass.prototype)`
  3. Wrap a class-instance-shaped value instead, or expose static data via `ClassType` `eagerAttrs`

Example fix

// before
const record = Object.create(null)
new ClassInstance(record) // TypeError: null-prototype object
// after
class Record {}
const record = Object.assign(new Record(), data)
new ClassInstance(record, { eagerAttrs: 'all' })
Defensive patterns

Strategy: type-guard

Validate before calling

const hasRealClass = (v: object): boolean => {
  const proto = Object.getPrototypeOf(v)
  return proto !== null && typeof (proto as { constructor?: unknown }).constructor === 'function'
}
if (!hasRealClass(candidate)) {
  // pass as a plain input instead of wrapping
  inputs.data = candidate
} else {
  inputs.obj = new ClassInstance(candidate)
}

Type guard

const isClassInstance = (v: object): boolean =>
  typeof (Object.getPrototypeOf(v) as { constructor?: unknown } | null)?.constructor === 'function'

Try / catch

try {
  wrapped = new ClassInstance(obj)
} catch (err) {
  if (err instanceof TypeError && err.message.includes('null-prototype object')) {
    // null-prototype records cross as plain values anyway
    wrapped = obj
  } else {
    throw err
  }
}

Prevention

When it happens

Trigger: Wrapping objects created via `Object.create(null)`, JSON-parse reviver outputs with stripped prototypes, or dictionary-style records built by this library's own walks (which deliberately use null-prototype objects); wrapping a `MontyClassProxy.attributes` record directly.

Common situations: Passing a key/value record that came from `Object.create(null)` (common in security-conscious code or sandboxes) as a `ClassInstance`; attempting to round-trip restored sandbox attribute records back into a new session as wrapped instances.

Related errors


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