{"record":{"id":"347cb6d3821b5cf3","repo":"dotnet/aspnetcore","slug":"the-property-identifier-is-not-writable","errorCode":null,"errorMessage":"The property '${identifier}' is not writable.","messagePattern":"The property '(.+?)' is not writable\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts","lineNumber":646,"sourceCode":"          }\n          throw new Error(`The value '${identifier}' is not a function.`);\n\n      case JSCallType.ConstructorCall:\n          const ctor = parent[memberName];\n          if (ctor instanceof Function) {\n              const bound = ctor.bind(parent);\n              return (...args: any[]) => new bound(...args);\n          }\n          throw new Error(`The value '${identifier}' is not a function.`);\n\n      case JSCallType.GetValue:\n          if (!isReadableProperty(parent, memberName)) {\n              throw new Error(`The property '${identifier}' is not defined or is not readable.`);\n          }\n          return () => parent[memberName];\n      case JSCallType.SetValue:\n          if (!isWritableProperty(parent, memberName)) {\n              throw new Error(`The property '${identifier}' is not writable.`);\n          }\n          return (...args: any[]) => parent[memberName] = args[0];\n      }\n  }\n\n  function isReadableProperty(obj: any, propName: string) {\n      // Return false for missing property.\n      if (!(propName in obj)) {\n          return false;\n      }\n\n      // If the property is present we examine its descriptor, potentially needing to walk up the prototype chain.\n      while (obj !== undefined) {\n          const descriptor = Object.getOwnPropertyDescriptor(obj, propName);\n\n          if (descriptor) {\n              // Return true for data property\n              if (descriptor.hasOwnProperty(\"value\")) {","sourceCodeStart":628,"sourceCodeEnd":664,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L628-L664","documentation":"Thrown in the JSCallType.SetValue branch when isWritableProperty(parent, memberName) returns false (line 645). isWritableProperty checks the data/accessor descriptor's 'writable' flag or presence of a 'set'; missing properties and read-only properties (e.g. frozen objects, getter-only accessors, built-in constants) are not writable.","triggerScenarios":"Writing to a read-only property (e.g. 'window.undefined', a frozen object's field), a getter-only accessor, or a property that doesn't exist yet on a non-extensible object. Also writing to a property whose prototype descriptor is configurable:false writable:false.","commonSituations":"Trying to assign to a DOM read-only property; mutating a frozen/sealed export; version change that made a property read-only; SSR where the global is a stub.","solutions":["Check Object.getOwnPropertyDescriptor; if writable is false or only a getter exists, do not write directly.","Expose a setter function in a JS shim and call it instead.","Make the target object extensible / use a mutable container.","Pick a different, writable property to hold the value."],"exampleFix":"// before\nawait JS.InvokeVoidAsync(\"window.myConfig\", newValue); // read-only\n\n// after\n// shim.js\nlet _cfg;\nexport function setConfig(v) { _cfg = v; }\nexport function getConfig() { return _cfg; }\n// C#: await JS.InvokeVoidAsync(\"shim.setConfig\", newValue);","handlingStrategy":"validation","validationCode":"// shim: safe set\nexport function setProp(obj, k, v) {\n  let o = obj;\n  while (o) { const d = Object.getOwnPropertyDescriptor(o, k); if (d) { if (!d.writable && !d.set) throw new TypeError(`${k} read-only`); break; } o = Object.getPrototypeOf(o); }\n  obj[k] = v;\n}","typeGuard":"function isWritable(o:any, k:string):boolean {\n  let cur = o;\n  while (cur) { const d = Object.getOwnPropertyDescriptor(cur, k); if (d) return !!d.writable || !!d.set; cur = Object.getPrototypeOf(cur); }\n  return Object.isExtensible(o);\n}","tryCatchPattern":"try { await JS.InvokeVoidAsync('obj.prop', val); } catch (e) { if (/not writable/i.test(e.message)) { /* use setter shim */ } else throw e; }","preventionTips":["Do not write to known read-only DOM properties; use setter functions.","Avoid mutating frozen/sealed exports.","Expose explicit setter functions in shims for mutable configuration."],"tags":["blazor","jsinterop","properties","descriptor","write","readonly"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}