dotnet/aspnetcore · error · Error

The property ' ' is not writable.

Error message

The property '${identifier}' is not writable.

What it means

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.

Solutions

  1. Check the descriptor: Object.getOwnPropertyDescriptor(window.myObj, 'prop').writable.
  2. If read-only by design, expose a setter or a method that mutates internally.
  3. Remove Object.freeze / Object.preventExtensions on the target if mutation is intended.

Example fix

// before (JS)
Object.defineProperty(myObj, 'config', { value: 1, writable: false });
// C#: await JS.InvokeVoidAsync("myObj.config", 2); // throws

// after (JS)
Object.defineProperty(myObj, 'config', { value: 1, writable: true });
Defensive patterns

Strategy: validation

Validate before calling

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; }

Type guard

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; }

Try / catch

try { await JS.InvokeVoidAsync("myObj.config", value); } catch (JSException e) when (e.Message.Contains("is not writable")) { /* check descriptor.writable, freeze, or getter-only */ }

Prevention

When it happens

Trigger: .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.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/347cb6d3821b5cf3. Report an issue: GitHub.

Appendix: source

Thrown at src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts:646

          }
          throw new Error(`The value '${identifier}' is not a function.`);

      case JSCallType.ConstructorCall:
          const ctor = parent[memberName];
          if (ctor instanceof Function) {
              const bound = ctor.bind(parent);
              return (...args: any[]) => new bound(...args);
          }
          throw new Error(`The value '${identifier}' is not a function.`);

      case JSCallType.GetValue:
          if (!isReadableProperty(parent, memberName)) {
              throw new Error(`The property '${identifier}' is not defined or is not readable.`);
          }
          return () => parent[memberName];
      case JSCallType.SetValue:
          if (!isWritableProperty(parent, memberName)) {
              throw new Error(`The property '${identifier}' is not writable.`);
          }
          return (...args: any[]) => parent[memberName] = args[0];
      }
  }

  function isReadableProperty(obj: any, propName: string) {
      // Return false for missing property.
      if (!(propName in obj)) {
          return false;
      }

      // If the property is present we examine its descriptor, potentially needing to walk up the prototype chain.
      while (obj !== undefined) {
          const descriptor = Object.getOwnPropertyDescriptor(obj, propName);

          if (descriptor) {
              // Return true for data property
              if (descriptor.hasOwnProperty("value")) {

View on GitHub (pinned to 3600ca084e)