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
- Check Object.isFrozen(target) / Object.isExtensible(target) before calling InterceptProperty and skip frozen objects.
- Make sure only one instrumentation system touches the same property at a time; dispose other interceptors first.
- Register hooks during setup, before any freeze/seal of the target.
- If you own the object, keep it extensible while instrumentation is active.
- 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
- Register all interceptors before any Object.freeze/preventExtensions call.
- Avoid two instrumentation systems targeting the same property concurrently.
- Check Object.isExtensible(target) immediately before intercepting.
- Freeze objects only after all disposables have been disposed.
- Consider a Proxy wrapper as an alternative that cannot fail at define time.
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
- Property "${propertyKey.toString()}" of object "${target}" i
- Property "${propertyKey.toString()}" of object "${target}" i
- Property "${propertyKey.toString()}" of object "${target}" i
- Failed to restore original property descriptor "${propertyKe
- Failed to restore original function "${propertyKey.toString(
AI-assisted analysis of BabylonJS/Babylon.js@0592b347b8 (2026-08-30).
Data as JSON: /api/errors/d61cb7d1c6594273.
Report an issue: GitHub.