{"record":{"id":"290985d6a2f4560f","repo":"pydantic/monty","slug":"classinstance-expects-an-instance-of-a-class-not-a-null","errorCode":null,"errorMessage":"ClassInstance expects an instance of a class, not a null-prototype object","messagePattern":"ClassInstance expects an instance of a class, not a null-prototype object","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/classInstance.ts","lineNumber":217,"sourceCode":" */\nexport class ClassInstance extends BaseWrapper {\n  /** The instance's sandbox identity: reuse one wrapper to re-send an object\n   *  under the same id; reusing an id for a different object throws\n   *  `TypeError`. */\n  readonly id: string\n  /** The [`ClassType`] wrapper carrying the class's identity and policies:\n   *  `options.classType` if given, else a default one materialized from the\n   *  constructor. */\n  readonly classType: ClassType\n\n  declare readonly options: ClassInstanceOptions\n\n  constructor(instance: object, options: ClassInstanceOptions = {}) {\n    super(instance, options)\n    this.id = options.id === undefined ? generateUuid() : normalizeId('ClassInstance', options.id)\n    const ctor = classOf(instance)\n    if (ctor === undefined) {\n      throw new TypeError('ClassInstance expects an instance of a class, not a null-prototype object')\n    }\n    if (options.classType !== undefined) {\n      if (options.classType.classType !== ctor) {\n        throw new TypeError(\"classType does not match the instance's class\")\n      }\n      if (options.name !== undefined) {\n        throw new TypeError('pass name on the ClassType wrapper, not alongside classType')\n      }\n      this.classType = options.classType\n    } else {\n      this.classType = new ClassType(ctor as new (...args: never[]) => object, { name: options.name })\n    }\n  }\n\n  /** Class name shown to the sandbox: the class wrapper's, so the instance,\n   *  its type object and error messages all agree. */\n  override getName(): string {\n    return this.classType.getName()","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/classInstance.ts#L199-L235","documentation":"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`.","triggerScenarios":"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.","commonSituations":"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.","solutions":["If it is just data, pass the plain record directly as a session input — plain objects cross without a wrapper","Give the object a real prototype/class: build it from a class or add `Object.setPrototypeOf(obj, SomeClass.prototype)`","Wrap a class-instance-shaped value instead, or expose static data via `ClassType` `eagerAttrs`"],"exampleFix":"// before\nconst record = Object.create(null)\nnew ClassInstance(record) // TypeError: null-prototype object\n// after\nclass Record {}\nconst record = Object.assign(new Record(), data)\nnew ClassInstance(record, { eagerAttrs: 'all' })","handlingStrategy":"type-guard","validationCode":"const hasRealClass = (v: object): boolean => {\n  const proto = Object.getPrototypeOf(v)\n  return proto !== null && typeof (proto as { constructor?: unknown }).constructor === 'function'\n}\nif (!hasRealClass(candidate)) {\n  // pass as a plain input instead of wrapping\n  inputs.data = candidate\n} else {\n  inputs.obj = new ClassInstance(candidate)\n}","typeGuard":"const isClassInstance = (v: object): boolean =>\n  typeof (Object.getPrototypeOf(v) as { constructor?: unknown } | null)?.constructor === 'function'","tryCatchPattern":"try {\n  wrapped = new ClassInstance(obj)\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes('null-prototype object')) {\n    // null-prototype records cross as plain values anyway\n    wrapped = obj\n  } else {\n    throw err\n  }\n}","preventionTips":["Avoid `Object.create(null)` for objects destined for the sandbox; use `{}` or a class","Do not re-wrap `MontyClassProxy.attributes` or other null-prototype records produced by the library","Check `Object.getPrototypeOf(x)?.constructor` before choosing ClassInstance vs plain input","Remember JSON.parse results DO have Object.prototype — this error is specific to prototype-stripped objects"],"tags":["typescript","constructor","prototype"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"adc986b362e3961f407868cb118a99fe831b9e61","analyzedAt":"2026-09-13T19:19:18.698Z","contentChangedAt":"2026-09-13T19:19:18.698Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}