BabylonJS/Babylon.js · error

Property "${propertyKey.toString()}" of object "${target}" i

Error message

Property "${propertyKey.toString()}" of object "${target}" is not configurable.

What it means

InterceptProperty redefines a property with a getter/setter wrapper to run hooks; this is only possible for configurable properties. When the property already exists (directly or inherited) and its descriptor reports configurable:false, the library refuses to intercept it and throws instead of silently failing to install hooks.

Source

Thrown at packages/dev/inspector-v2/src/instrumentation/propertyInstrumentation.ts:85

    const ownerAndDescriptor = GetPropertyDescriptor(target, propertyKey);

    // If the property does not exist, we'll define one transiently directly on the target object.
    const [propertyOwner, propertyDescriptor] = ownerAndDescriptor ?? [
        target,
        {
            configurable: true,
            enumerable: true,
            writable: true,
            value: undefined,
        },
    ];

    if (!ownerAndDescriptor) {
        Reflect.defineProperty(propertyOwner, propertyKey, propertyDescriptor);
    } else {
        // If the property is not configurable, it cannot be intercepted.
        if (!propertyDescriptor.configurable) {
            throw new Error(`Property "${propertyKey.toString()}" of object "${target}" is not configurable.`);
        }

        // If the property is not writable, it cannot be intercepted, but it cannot be mutated anyway so there is no need to intercept it.
        if (IsPropertyReadonly(propertyDescriptor)) {
            return {
                dispose: () => {},
            };
        }
    }

    // Get or create the hooks map for the target object.
    let hooksMap = InterceptorHooksMaps.get(target);
    if (!hooksMap) {
        InterceptorHooksMaps.set(target, (hooksMap = new Map()));
    }

    // Get or create the hooks array for the property key.
    let hooksForKey = hooksMap.get(propertyKey);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Verify with Object.getOwnPropertyDescriptor(owner, key).configurable before watching; skip non-configurable keys.
  2. Avoid Object.freeze/seal on objects whose properties you want to watch.
  3. If you control the definition, define the property with configurable:true.
  4. Watch an owning wrapper object or use a Proxy around the target instead of intercepting the property directly.
  5. If the property is readonly, note InterceptProperty silently returns a no-op disposable; only configurable but writable:false paths throw here.

Example fix

// before
const state = Object.freeze({ value: 1 });
InterceptProperty(state, "value", { afterSet: log }); // throws

// after
const state = { value: 1 }; // keep configurable
const d = InterceptProperty(state, "value", { afterSet: log });
d.dispose();
Defensive patterns

Strategy: validation

Validate before calling

function isInterceptable(target: object, key: PropertyKey): boolean {
  let owner: object | null = target;
  while (owner) {
    const d = Reflect.getOwnPropertyDescriptor(owner, key);
    if (d) return d.configurable === true;
    owner = Reflect.getPrototypeOf(owner);
  }
  return true; // nonexistent props are created transiently
}
if (!isInterceptable(obj, "value")) throw new Error("skip interception");

Type guard

function isConfigurableProperty(target: object, key: PropertyKey): boolean {
  const d = Reflect.getOwnPropertyDescriptor(target, key);
  return d ? d.configurable : true;
}

Try / catch

let disposable: IDisposable;
try {
  disposable = InterceptProperty(obj, key, hooks);
} catch (e) {
  if (e instanceof Error && /is not configurable/.test(e.message)) {
    disposable = { dispose: () => {} }; // gracefully skip
  } else throw e;
}

Prevention

When it happens

Trigger: Calling InterceptProperty (directly or via watchProperty) on a property declared with configurable:false — e.g. class methods/fields using decorators that seal them, frozen objects, built-in objects like Math/JSON properties, or properties created via Object.defineProperty without configurable:true.

Common situations: Watching properties on frozen state objects, on built-in host objects (DOM elements, Math, window.location), on TypeScript 'readonly' fields emitted with writable:false plus configurable:false, or third-party objects hardened by libraries like @ungap or Immer.

Related errors


AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30). Data as JSON: /api/errors/66d972a6a04159e4. Report an issue: GitHub.