BabylonJS/Babylon.js · error

Failed to define new property "${propertyKey.toString()}" on

Error message

Failed to define new property "${propertyKey.toString()}" on object "${target}".

What it means

After checking configurability, InterceptProperty installs the hook by redefining the property with a get/set pair via Reflect.defineProperty. This error means that call returned false — the redefinition was rejected at the last step, typically because the object is non-extensible or the property became non-configurable concurrently, so no hooks could be installed.

Source

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

        if (
            // Replace the property with a new one that calls the hooks in addition to the original getter and setter.
            !Reflect.defineProperty(target, propertyKey, {
                configurable: true,
                get: getValue
                    ? function (this: unknown) {
                          return getValue!.call(this);
                      }
                    : undefined,
                set: function (this: unknown, newValue: unknown) {
                    setValue.call(this, newValue);
                    for (const { afterSet } of hooksForKey!) {
                        afterSet?.(newValue);
                    }
                },
            })
        ) {
            throw new Error(`Failed to define new property "${propertyKey.toString()}" on object "${target}".`);
        }
    }
    hooksForKey.push(hooks as PropertyHooks<unknown>);

    let isDisposed = false;
    return {
        dispose: () => {
            if (!isDisposed) {
                // Remove the hooks from the hooks array for the property key.
                hooksForKey.splice(hooksForKey.indexOf(hooks as PropertyHooks<unknown>), 1);

                // If there are no more hooks for the property key, remove the property from the hooks map.
                if (hooksForKey.length === 0) {
                    hooksMap.delete(propertyKey);

                    // If there are no more hooks for the target object, remove the hooks map from the WeakMap.
                    if (hooksMap.size === 0) {
                        InterceptorHooksMaps.delete(target);

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Check Object.isFrozen(target) / Object.isExtensible(target) before calling InterceptProperty and skip frozen objects.
  2. Make sure only one instrumentation system touches the same property at a time; dispose other interceptors first.
  3. Register hooks during setup, before any freeze/seal of the target.
  4. If you own the object, keep it extensible while instrumentation is active.
  5. Catch the error and fall back to polling/dirty-checking the value instead of property interception.

Example fix

// before
const store = createStore();
Object.freeze(store); // too early
const d = InterceptProperty(store, "count", { afterSet: log }); // throws

// after
const store = createStore();
const d = InterceptProperty(store, "count", { afterSet: log });
d.dispose();
Object.freeze(store);
Defensive patterns

Strategy: validation

Validate before calling

function canRedefine(target: object, key: PropertyKey): boolean {
  if (!Object.isExtensible(target)) return false;
  const d = Reflect.getOwnPropertyDescriptor(target, key);
  return !d || d.configurable;
}
if (!canRedefine(store, "count")) throw new Error("object not redefinable");

Type guard

function isRedefinable(target: object, key: PropertyKey): target is object {
  return Object.isExtensible(target) &&
    (!(key in target) || Reflect.getOwnPropertyDescriptor(target, key)!.configurable);
}

Try / catch

let disposable: IDisposable;
try {
  disposable = InterceptProperty(obj, key, hooks);
} catch (e) {
  if (e instanceof Error && /Failed to define new property/.test(e.message)) {
    disposable = { dispose: () => {} }; // fall back to polling the value
  } else throw e;
}

Prevention

When it happens

Trigger: Reflect.defineProperty on the target returns false: usually Object.freeze/Object.preventExtensions on the target between descriptor lookup and definition, a conflicting property redefinition by other code, or (in rare engine cases) an invalid descriptor combination for a non-configurable existing property.

Common situations: Race conditions where another interceptor (or devtools/mock framework) rewrites the same property at the same time; freezing a store object after starting but before hook registration; intercepting properties on objects made non-extensible by class decorators or hardening utilities.

Related errors


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