pydantic/monty · error · TypeError

classType does not match the instance's class

Error message

classType does not match the instance's class

What it means

ClassInstance accepts an optional classType option — a ClassType wrapper that must wrap exactly the same constructor as the instance being wrapped. This TypeError is thrown when the ClassType wrapper's underlying class (`options.classType.classType`) is not identical (===) to the constructor found on the instance's prototype chain, preventing an instance from crossing the wire with a mismatched type identity.

Source

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

   *  `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()
  }
}

/** Options for [`ClassType`]: the inherited policies applied to the class

View on GitHub (pinned to adc986b362)

Solutions

  1. Make the ClassType wrap the exact same class as the instance: `new ClassInstance(user, { classType: new ClassType(User) })` where `user instanceof User`.
  2. Omit the classType option entirely — ClassInstance materializes a default ClassType from the instance's constructor, inheriting options.name.
  3. Check `new ClassType(X).classType === instance.constructor` before constructing the wrapper.
  4. If the class crosses realms/bundles, use one canonical copy of the class so identity comparison succeeds.

Example fix

// before
new ClassInstance(baseUser, { classType: userType /* ClassType(AdminUser) */ }) // TypeError
// after
new ClassInstance(baseUser, { classType: new ClassType(User, { name: 'User' }) })
Defensive patterns

Strategy: validation

Validate before calling

function isValidClassInstancePair(instance: object, classType: ClassType): boolean {
  return classType.classType === (Object.getPrototypeOf(instance)?.constructor)
}

Type guard

function wrapsSameClass(instance: object, classType: ClassType): boolean {
  const ctor = Object.getPrototypeOf(instance)?.constructor
  return typeof ctor === 'function' && ctor === classType.classType
}

Try / catch

try {
  const wrapper = new ClassInstance(instance, { classType })
} catch (e) {
  if (e instanceof TypeError && /classType does not match/.test(e.message)) {
    // fall back to the default ClassType materialized from the instance
    const wrapper = new ClassInstance(instance)
  } else throw e
}

Prevention

When it happens

Trigger: Calling `new ClassInstance(instance, { classType: new ClassType(SomeOtherClass) })` where SomeOtherClass !== instance's actual constructor — e.g. reusing a ClassType wrapper built for a subclass while wrapping a base-class instance, copying a ClassType from a different domain/realm (the class functions differ by identity), or refactoring code so the classType variable now points at a different class.

Common situations: Centralizing a shared ClassType registry but wrapping the wrong instance; passing a parent ClassType for a subclass instance or vice versa; creating separate class copies (e.g. class re-declaration, module duplication via bundling) so === fails even though the names match.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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