{"record":{"id":"a23454ee0fda2291","repo":"BabylonJS/Babylon.js","slug":"property-propertykey-tostring-of-object-a23454","errorCode":null,"errorMessage":"Property \"${propertyKey.toString()}\" of object \"${target}\" is not configurable.","messagePattern":"Property \"(.+?)\" of object \"(.+?)\" is not configurable\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/dev/inspector-v2/src/instrumentation/functionInstrumentation.ts","lineNumber":46,"sourceCode":"): IDisposable;\r\n// Fallback overload for generic/dynamic cases where the function type cannot be inferred\r\nexport function InterceptFunction<T extends object>(target: T, propertyKey: keyof T, hooks: FunctionHooks): IDisposable;\r\n/** @internal */\r\nexport function InterceptFunction<T extends object>(target: T, propertyKey: keyof T, hooks: FunctionHooks): IDisposable {\r\n    if (!hooks.afterCall) {\r\n        throw new Error(\"At least one hook must be provided.\");\r\n    }\r\n\r\n    const originalFunction = Reflect.get(target, propertyKey, target) as (...args: any) => any;\r\n    if (typeof originalFunction !== \"function\") {\r\n        throw new Error(`Property \"${propertyKey.toString()}\" of object \"${target}\" is not a function.`);\r\n    }\r\n\r\n    // Make sure the property is configurable and writable, otherwise it is immutable and cannot be intercepted.\r\n    const propertyDescriptor = Reflect.getOwnPropertyDescriptor(target, propertyKey);\r\n    if (propertyDescriptor) {\r\n        if (!propertyDescriptor.configurable) {\r\n            throw new Error(`Property \"${propertyKey.toString()}\" of object \"${target}\" is not configurable.`);\r\n        }\r\n\r\n        if (propertyDescriptor.writable === false || (propertyDescriptor.writable === undefined && !propertyDescriptor.set)) {\r\n            throw new Error(`Property \"${propertyKey.toString()}\" of object \"${target}\" is readonly.`);\r\n        }\r\n    }\r\n\r\n    // Get or create the hooks map for the target object.\r\n    let hooksMap = InterceptorHooksMaps.get(target);\r\n    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","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/BabylonJS/Babylon.js/blob/0592b347b8a4ee0236089ea86a749cacfdb266d8/packages/dev/inspector-v2/src/instrumentation/functionInstrumentation.ts#L28-L64","documentation":"Function instrumentation works by redefining the property on the target with Reflect.defineProperty, which is only possible for configurable properties. If Reflect.getOwnPropertyDescriptor returns a descriptor whose configurable flag is false, the property is immutable and the library refuses to intercept it with this error. This prevents silently failing to swap the function.","triggerScenarios":"Calling InterceptFunction on a property that was defined with configurable: false — e.g. built-ins like Function.prototype.call, methods defined via Object.defineProperty with configurable:false, frozen (Object.freeze) or sealed (Object.seal) objects, or class fields made non-configurable.","commonSituations":"Trying to hook native/built-in methods that are non-configurable in some engines; intercepting methods on objects passed through Object.freeze for immutability; instrumenting module exports or namespace objects; attempts to hook secure/hardened objects.","solutions":["Intercept a configurable wrapper: create your own object whose method delegates to the original and intercept that instead.","Unfreeze/clone the object: const copy = {...target} (restores configurability for own props) and intercept the copy, then route callers through it.","If you own the definition, change Object.defineProperty options to configurable: true (and writable: true).","For prototype methods, intercept on the prototype only if that descriptor is configurable; otherwise wrap at the call site."],"exampleFix":"// before\nObject.defineProperty(obj, 'play', { value: fn, configurable: false });\nInterceptFunction(obj, 'play', hooks);\n// after\nObject.defineProperty(obj, 'play', { value: fn, configurable: true, writable: true });\nInterceptFunction(obj, 'play', hooks);","handlingStrategy":"validation","validationCode":"function isConfigurable(target, propertyKey) {\n  const d = Reflect.getOwnPropertyDescriptor(target, propertyKey);\n  return !d || d.configurable;\n}\nif (!isConfigurable(target, 'play')) throw new TypeError('target.play is not configurable');","typeGuard":"function hasConfigurableDescriptor(target: object, key: PropertyKey): boolean {\n  const d = Object.getOwnPropertyDescriptor(target, key);\n  return d === undefined || d.configurable === true;\n}","tryCatchPattern":"try {\n  InterceptFunction(target, propertyKey, hooks);\n} catch (e) {\n  if (String((e as Error)?.message).includes('is not configurable')) {\n    console.warn(`Skipping interception of ${String(propertyKey)}: non-configurable property`);\n    return null;\n  }\n  throw e;\n}","preventionTips":["Check Object.getOwnPropertyDescriptor(target, key)?.configurable before intercepting.","Do not call Object.freeze/Object.seal on objects you plan to instrument.","Avoid intercepting module namespace objects and engine built-ins with non-configurable methods.","Intercept a wrapper object you own instead of hardened third-party objects."],"tags":["instrumentation","property-descriptor","frozen-object","function-interception"],"backgroundTag":"property-not-configurable","analyzedSha":"0592b347b8a4ee0236089ea86a749cacfdb266d8","analyzedAt":"2026-08-30T15:11:20.442Z","schemaVersion":2},"datasetVersion":"2026-08-30T18:17:15.746Z"}