dotnet/wpf · error · InvalidOperationException

SR.CannotWriteToReadOnly

Error message

SR.CannotWriteToReadOnly

What it means

While preparing to write through a binding path, PropertyPathWorker checks whether the resolved CLR property (PropertyInfo) is read-only and throws InvalidOperationException (SR.CannotWriteToReadOnly) if so. The library refuses writes to properties that have no setter.

Solutions

  1. Add a setter to the target property, or back it with a writable field and raise PropertyChanged
  2. Set the binding Mode to OneWay (default for most) so no write is attempted
  3. Bind to a different writable property

Example fix

// before
public string FullName => $"{First} {Last}";
// after
public string FullName
{
    get => $"{First} {Last}";
    set { /* parse or store */ OnPropertyChanged(); }
}
Defensive patterns

Strategy: validation

Validate before calling

bool canWrite = item.GetType().GetProperty(propName)?.CanWrite == true;
if (!canWrite) throw new InvalidOperationException($"{propName} has no setter; use OneWay binding");

Prevention

When it happens

Trigger: A TwoWay or OneWayToSource binding (or direct SetValue on the path) targeting a get-only property, e.g. new Binding("FullName") { Mode = TwoWay } where FullName has only a getter.

Common situations: Binding to computed/read-only model properties, forgetting Mode=OneWay, calling ItemContainerStyle setters against read-only properties, changes after a property lost its setter in a refactor.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/91cfaccdc619de4a. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Data/PropertyPathWorker.cs:1581

                    if (pd == null)
                        dpa = info as DynamicPropertyAccessor;
                }
            }
        }

        private void CheckReadOnly(object item, object info)
        {
            PropertyInfo pi;
            PropertyDescriptor pd;
            DependencyProperty dp;
            DynamicPropertyAccessor dpa;
            SetPropertyInfo(info, out pi, out pd, out dp, out dpa);

            if (pi != null)
            {
                if (IsPropertyReadOnly(item, pi))
                    throw new InvalidOperationException(SR.Format(SR.CannotWriteToReadOnly, item.GetType(), pi.Name));
            }
            else if (pd != null)
            {
                if (pd.IsReadOnly)
                    throw new InvalidOperationException(SR.Format(SR.CannotWriteToReadOnly, item.GetType(), pd.Name));
            }
            else if (dp != null)
            {
                if (dp.ReadOnly)
                    throw new InvalidOperationException(SR.Format(SR.CannotWriteToReadOnly, item.GetType(), dp.Name));
            }
            else if (dpa != null)
            {
                if (dpa.IsReadOnly)
                    throw new InvalidOperationException(SR.Format(SR.CannotWriteToReadOnly, item.GetType(), dpa.PropertyName));
            }
        }

View on GitHub (pinned to 81131a70a4)