{"record":{"id":"76d3d037753b637a","repo":"BabylonJS/Babylon.js","slug":"failed-to-define-new-function-propertykey-tostr","errorCode":null,"errorMessage":"Failed to define new function \"${propertyKey.toString()}\" on object \"${target}\".","messagePattern":"Failed to define new function \"(.+?)\" on object \"(.+?)\"\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/inspector-v2/src/instrumentation/functionInstrumentation.ts","lineNumber":74,"sourceCode":"    if (!hooksMap) {\r\n        InterceptorHooksMaps.set(target, (hooksMap = new Map()));\r\n    }\r\n\r\n    // Get or create the hooks array for the property key.\r\n    let hooksForKey = hooksMap.get(propertyKey);\r\n    if (!hooksForKey) {\r\n        hooksMap.set(propertyKey, (hooksForKey = []));\r\n        if (\r\n            // Replace the function with a new one that calls the hooks in addition to the original function.\r\n            !Reflect.set(target, propertyKey, function (this: unknown, ...args: unknown[]) {\r\n                const result = Reflect.apply(originalFunction, this, args);\r\n                for (const { afterCall } of hooksForKey!) {\r\n                    afterCall?.(...args);\r\n                }\r\n                return result;\r\n            })\r\n        ) {\r\n            throw new Error(`Failed to define new function \"${propertyKey.toString()}\" on object \"${target}\".`);\r\n        }\r\n    }\r\n    hooksForKey.push(hooks as FunctionHooks<unknown[]>);\r\n\r\n    let isDisposed = false;\r\n    return {\r\n        dispose: () => {\r\n            if (!isDisposed) {\r\n                // Remove the hooks from the hooks array for the property key.\r\n                hooksForKey.splice(hooksForKey.indexOf(hooks), 1);\r\n\r\n                // If there are no more hooks for the property key, remove the property from the hooks map.\r\n                if (hooksForKey.length === 0) {\r\n                    hooksMap.delete(propertyKey);\r\n\r\n                    // If there are no more hooks for the target object, remove the hooks map from the WeakMap.\r\n                    if (hooksMap.size === 0) {\r\n                        InterceptorHooksMaps.delete(target);\r","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/inspector-v2/src/instrumentation/functionInstrumentation.ts#L56-L92","documentation":"After validating the property, InterceptFunction installs the interceptor by redefining the property with Reflect.defineProperty; the property is only considered intercepted if that call returns true. If the redefinition fails, this error is thrown. This usually means the descriptor check passed but the engine still refused the redefine (e.g. Proxy invariants, exotic objects, or a change between the descriptor check and the define).","triggerScenarios":"Reflect.defineProperty(target, propertyKey, replacementDescriptor) returning false during InterceptFunction — e.g. the target is a Proxy whose invariant constraints reject the redefinition, the property changed between getOwnPropertyDescriptor and defineProperty, or the target is an exotic/builtin object that disallows redefining the property.","commonSituations":"Instrumenting objects behind a Proxy (invariant violations); hooking host/builtin objects (DOM, Node internals) with unusual internal slots; concurrent code redefining the same property mid-interception; intercepting properties on module namespace objects.","solutions":["Intercept the underlying raw object, not a Proxy wrapper around it.","Re-check that the property is still configurable/writable immediately before intercepting; avoid racing other code that redefines it.","For builtin/host objects, wrap them in your own plain object with delegating methods and intercept that.","Catch this error and fall back to manual instrumentation (call hooks.afterCall explicitly at the call site)."],"exampleFix":"// before\nInterceptFunction(proxyWrappedPlayer, 'play', hooks); // defineProperty fails on proxy invariants\n// after\nInterceptFunction(rawPlayer, 'play', hooks); // intercept the raw target, not the proxy","handlingStrategy":"try-catch","validationCode":"function isPlainInterceptableTarget(target: object, key: PropertyKey): boolean {\n  const d = Object.getOwnPropertyDescriptor(target, key);\n  return (!d || (d.configurable && d.writable !== false)) && !Symbol.toStringTag; // also avoid proxies/exotic targets\n}\n// check before intercepting:\nif (!isPlainInterceptableTarget(target, 'play')) throw new TypeError('target not safely redefinable');","typeGuard":"function isRawInterceptable(target: object, key: PropertyKey): boolean {\n  if (typeof Proxy !== 'undefined') {\n    try { Object.getOwnPropertyDescriptor(target, key); } catch { return false; } // proxies may throw on invariants\n  }\n  const d = Object.getOwnPropertyDescriptor(target, key);\n  return !d || (d.configurable && d.writable !== false);\n}","tryCatchPattern":"try {\n  const handle = InterceptFunction(target, propertyKey, hooks);\n  return handle;\n} catch (e) {\n  if (String((e as Error)?.message).includes('Failed to define new function')) {\n    console.warn(`Redefinition failed for ${String(propertyKey)}; falling back to manual hooks`);\n    return null; // call hooks.afterCall manually at the call site\n  }\n  throw e;\n}","preventionTips":["Intercept raw objects, not Proxy wrappers or exotic host/builtin objects.","Minimize the window between descriptor validation and interception to avoid races.","Do not intercept module namespace objects or objects with internal slots.","Test interception in a try/catch during setup so instrumentation failures degrade gracefully."],"tags":["instrumentation","defineproperty","proxy","function-interception"],"backgroundTag":"defineproperty-failed","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}