{"record":{"id":"e92a464bb653a688","repo":"pydantic/monty","slug":"classinstance-expects-an-object-instance","errorCode":null,"errorMessage":"ClassInstance expects an object instance","messagePattern":"ClassInstance expects an object instance","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/monty-js/ts/classInstance.ts","lineNumber":78,"sourceCode":"  /** A [`ClassType`] wrapper for the instance's class, overriding the default\n   *  one materialized from the constructor — pass one to grant class-level\n   *  policies (or a pinned class id) alongside the instance. Must wrap the\n   *  instance's own constructor. Its eager class attrs are sent with every\n   *  crossing of the instance, so `type(x)` in the sandbox sees them. */\n  classType?: ClassType\n}\n\n/** Shared behavior of [`ClassInstance`] and [`ClassType`]: the wrapped value,\n *  the attr/method exposure policies, and the dispatch entry points the\n *  session layer calls (`getEagerAttrs`, `lookupLazyAttr`, `callMethod`). */\nexport abstract class BaseWrapper {\n  constructor(\n    /** The wrapped host object; returned unchanged when the sandbox returns the instance. */\n    readonly instance: object,\n    readonly options: BaseWrapperOptions = {},\n  ) {\n    if ((typeof instance !== 'object' && typeof instance !== 'function') || instance === null) {\n      throw new TypeError('ClassInstance expects an object instance')\n    }\n    validatePolicy('eagerAttrs', options.eagerAttrs)\n    validatePolicy('lazyAttrs', options.lazyAttrs)\n    validatePolicy('allowedMethods', options.allowedMethods)\n  }\n\n  /** Class name shown to the sandbox: `options.name`, else the constructor name. */\n  getName(): string {\n    if (this.options.name !== undefined) {\n      return this.options.name\n    }\n    return constructorName(this.instance)\n  }\n\n  /** The `[name, value]` attr pairs sent into the sandbox with the instance,\n   *  each value already passed through `convertValue`. */\n  getEagerAttrs(): Array<[string, unknown]> {\n    const policy = this.options.eagerAttrs","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/pydantic/monty/blob/adc986b362e3961f407868cb118a99fe831b9e61/crates/monty-js/ts/classInstance.ts#L60-L96","documentation":"A `TypeError` from the `BaseWrapper` constructor: `ClassInstance` (and `ClassType` via the shared base) only wraps host objects or functions, and the value passed as `instance` was neither — typically a primitive (`string`, `number`, `boolean`), `null`, or `undefined`. The wrapper must be able to store and route calls to a real object, so non-object values are rejected at construction time rather than failing later mid-session.","triggerScenarios":"`new ClassInstance(42)`, `new ClassInstance(null)`, `new ClassInstance(undefined)`, or passing a primitive returned by another API (e.g. `JSON.parse` output that is a number/string) where a class instance was expected. Note `typeof x === 'function'` is accepted, so plain functions and classes pass this check.","commonSituations":"Refactoring that changed a variable from an object to an id; optional values left `undefined`; deserialized data whose shape drifted; calling `new ClassInstance(obj.id)` instead of `new ClassInstance(obj)`.","solutions":["Pass the actual host object, not a primitive or its id","Guard optional values before wrapping: if the value can be null/undefined, skip wrapping or throw your own clearer error","If you only need a plain value in the sandbox, pass it directly as an input — do not wrap primitives in `ClassInstance`"],"exampleFix":"// before\nconst wrapper = user === undefined ? new ClassInstance(undefined) : new ClassInstance(user)\n// after\nif (user == null || typeof user !== 'object') throw new Error('expected a user object')\nconst wrapper = new ClassInstance(user, { eagerAttrs: 'all' })","handlingStrategy":"type-guard","validationCode":"function assertWrappable(v: unknown): asserts v is object {\n  if (v === null || (typeof v !== 'object' && typeof v !== 'function')) {\n    throw new TypeError(`ClassInstance needs an object, got ${v === null ? 'null' : typeof v}`)\n  }\n}\nassertWrappable(candidate)\nconst wrapper = new ClassInstance(candidate)","typeGuard":"const isWrappable = (v: unknown): v is object =>\n  v !== null && (typeof v === 'object' || typeof v === 'function')","tryCatchPattern":"try {\n  return new ClassInstance(candidate as object, opts)\n} catch (err) {\n  if (err instanceof TypeError && err.message.includes('expects an object instance')) {\n    // pass the raw value through instead of wrapping\n    return candidate\n  }\n  throw err\n}","preventionTips":["Type the wrapper input as `object` in your own code so primitives are rejected at compile time","Null-check optional values before wrapping","Do not wrap primitives 'to be safe' — pass them directly as session inputs"],"tags":["typescript","type-safety","constructor"],"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"}