{"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":"Error","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/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L628-L664","documentation":"Thrown by wrapJSCallAsFunction() in the SetValue case when isWritableProperty() returns false. A SetValue operation (.NET assigning a value to a JS property via interop) requires the property to be writable, have a setter, or be addable on an extensible object. Read-only properties, frozen objects, and write-only-missing scenarios are rejected.","triggerScenarios":".NET calls JS.InvokeVoidAsync('myObj.readOnlyProp', value) to set a property that is defined with writable:false, has only a getter, lives on a frozen/sealed object, or is on a non-extensible object without an existing data property.","commonSituations":"Assigning to a getter-only property; assigning to a property on Object.freeze'd objects; trying to add a new property to a non-extensible object; legacy code that mutated internals now locked down.","solutions":["Check the descriptor: Object.getOwnPropertyDescriptor(window.myObj, 'prop').writable.","If read-only by design, expose a setter or a method that mutates internally.","Remove Object.freeze / Object.preventExtensions on the target if mutation is intended."],"exampleFix":"// before (JS)\nObject.defineProperty(myObj, 'config', { value: 1, writable: false });\n// C#: await JS.InvokeVoidAsync(\"myObj.config\", 2); // throws\n\n// after (JS)\nObject.defineProperty(myObj, 'config', { value: 1, writable: true });","handlingStrategy":"validation","validationCode":"function isWritable(obj, prop) { if (!(prop in obj)) return Object.isExtensible(obj); let o = obj; while (o) { const d = Object.getOwnPropertyDescriptor(o, prop); if (d) return (d.writable === true) || (typeof d.set === 'function'); o = Object.getPrototypeOf(o); } return false; }","typeGuard":"function isWritableMember(obj: any, prop: string): boolean { if (!(prop in obj)) return Object.isExtensible(obj); let o: any = obj; while (o) { const d = Object.getOwnPropertyDescriptor(o, prop); if (d) return d.writable === true || typeof d.set === 'function'; o = Object.getPrototypeOf(o); } return false; }","tryCatchPattern":"try { await JS.InvokeVoidAsync(\"myObj.config\", value); } catch (JSException e) when (e.Message.Contains(\"is not writable\")) { /* check descriptor.writable, freeze, or getter-only */ }","preventionTips":["Check the property descriptor's writable flag before assigning via interop.","Avoid Object.freeze/preventExtensions on objects that .NET must mutate.","Expose setters or mutating methods for encapsulated state."],"tags":["js-interop","blazor","identifier","property","readonly"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}