BabylonJS/Babylon.js · error

Failed to delete transient property descriptor "${propertyKe

Error message

Failed to delete transient property descriptor "${propertyKey.toString()}" on object "${target}".

What it means

On final dispose, if the intercepted property did not exist on the target originally (it was inherited or newly transient), the library deletes the transient accessor with Reflect.deleteProperty so lookups fall back to the prototype chain. This error means the delete returned false, so the transient property remains on the object and shadows the prototype member.

Source

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

                    if (hooksMap.size === 0) {
                        InterceptorHooksMaps.delete(target);
                    }

                    const shouldRestorePropertyDescriptor =
                        // If the property is owned by the target object, then we may have replaced an original property descriptor that needs to be restore.
                        propertyOwner === target &&
                        // But this is only the case if we found an existing property descriptor on the target object (hence the ownerAndDescriptor check),
                        // or if the property value is not undefined, in which case we still want to retain the value that was set.
                        (ownerAndDescriptor || target[propertyKey] !== undefined);
                    // Otherwise, the property was inherited through the prototype chain, and so we can simply delete it from the target object.

                    if (shouldRestorePropertyDescriptor) {
                        if (!Reflect.defineProperty(target, propertyKey, propertyDescriptor)) {
                            throw new Error(`Failed to restore original property descriptor "${propertyKey.toString()}" on object "${target}".`);
                        }
                    } else {
                        if (!Reflect.deleteProperty(target, propertyKey)) {
                            throw new Error(`Failed to delete transient property descriptor "${propertyKey.toString()}" on object "${target}".`);
                        }
                    }
                }

                isDisposed = true;
            }
        },
    };
}

View on GitHub (pinned to 0592b347b8)

Solutions

  1. Dispose all InterceptProperty disposables before freezing/sealing the target.
  2. Verify Object.isFrozen(target) before disposal; unfreeze or rebuild if frozen.
  3. Ensure no other code redefines the transient property to configurable:false while hooked.
  4. Capture the state and rebuild the object if deleteProperty keeps failing.
  5. As a workaround, set the transient property to undefined instead of relying on deletion, then recreate the object cleanly.

Example fix

// before
const d = InterceptProperty(instance, "inheritedProp", { afterSet: log });
Object.freeze(instance);
d.dispose(); // throws

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

Strategy: try-catch

Validate before calling

// before dispose
const d = Reflect.getOwnPropertyDescriptor(target, key);
if (d && !d.configurable) console.warn("transient property cannot be deleted; defer or rebuild");
if (Object.isFrozen(target)) console.warn("frozen target; deleteProperty will fail");

Type guard

function canDeleteProperty(target: object, key: PropertyKey): boolean {
  if (Object.isFrozen(target)) return false;
  const d = Reflect.getOwnPropertyDescriptor(target, key);
  return !d || d.configurable;
}

Try / catch

try {
  disposable.dispose();
} catch (e) {
  if (e instanceof Error && /Failed to delete transient property descriptor/.test(e.message)) {
    try { delete (target as any)[key]; } catch { /* recreate object */ }
  } else throw e;
}

Prevention

When it happens

Trigger: Disposing InterceptProperty where the property was transient, but the target became frozen/sealed (non-configurable own property cannot be deleted), or the transient property was redefined with configurable:false while hooked.

Common situations: Freezing a scene/store object while property watchers are active; teardown ordering where a hardening step runs before disposals; another interceptor converting the transient accessor to a non-configurable one.

Related errors


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