dotnet/maui · error · InvalidOperationException

The BindableProperty "{property.PropertyName}" is readonly.

Error message

The BindableProperty "{property.PropertyName}" is readonly.

What it means

InvalidOperationException thrown by CoerceValue(BindableProperty, checkAccess:true) when the property is read-only. The public CoerceValue(BindableProperty) passes checkAccess:true, so attempting to coerce a read-only BindableProperty through that overload is rejected; read-only properties must be coerced via their BindablePropertyKey. The message names the offending property.

Source

Thrown at src/Controls/src/Core/BindableObject.cs:855

		/// <exception cref="ArgumentNullException">Thrown when <paramref name="propertyKey"/> is <see langword="null"/>.</exception>
		/// <exception cref="InvalidOperationException">Thrown when the bindable property identified by <paramref name="propertyKey"/> is read-only.</exception>
		/// <exception cref="ArgumentException">Thrown when the value is invalid according to the assigned logic in <see cref="BindableProperty.ValidateValueDelegate"/>.</exception>
		/// <remarks>If <see cref="BindableProperty.CoerceValueDelegate"/> is not assigned to, nothing will happen.</remarks>
		public void CoerceValue(BindablePropertyKey propertyKey)
		{
			if (propertyKey == null)
				throw new ArgumentNullException(nameof(propertyKey));

			CoerceValue(propertyKey.BindableProperty, checkAccess: false);
		}

		void CoerceValue(BindableProperty property, bool checkAccess)
		{
			if (property == null)
				throw new ArgumentNullException(nameof(property));

			if (checkAccess && property.IsReadOnly)
				throw new InvalidOperationException($"The BindableProperty \"{property.PropertyName}\" is readonly.");

			BindablePropertyContext bpcontext = GetContext(property);
			if (bpcontext == null)
				return;

			object currentValue = bpcontext.Values.GetValue();

			if (property.ValidateValue != null && !property.ValidateValue(this, currentValue))
				throw new ArgumentException($"Value is an invalid value for {property.PropertyName}", nameof(currentValue));

			property.CoerceValue?.Invoke(this, currentValue);
		}

		[Flags]
		internal enum BindableContextAttributes
		{
			IsBeingSet = 1 << 1,
			//GO AWAY

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Use the CoerceValue(BindablePropertyKey) overload for read-only properties, passing the key returned from CreateReadOnly.
  2. Expose the key (e.g. as a protected/internal property) so external coercers can call the key-based overload.
  3. If you control the property, decide whether it truly needs to be read-only.

Example fix

// before
obj.CoerceValue(MyControl.ReadOnlyProp); // IsReadOnly=true -> throws
// after
obj.CoerceValue(MyControl.ReadOnlyPropKey); // key-based overload, checkAccess=false
Defensive patterns

Strategy: type-guard

Validate before calling

if (property.IsReadOnly)
    throw new InvalidOperationException("Use the BindablePropertyKey overload for read-only properties.");
obj.CoerceValue(property);

Type guard

static bool IsCoerciblePublicly(Microsoft.Maui.Controls.BindableProperty p) => p is not null && !p.IsReadOnly;

Try / catch

try { obj.CoerceValue(prop); }
catch (InvalidOperationException) { obj.CoerceValue(propKey); }

Prevention

When it happens

Trigger: Calling the public CoerceValue(someReadOnlyBindableProperty) on a property declared with BindableProperty.CreateReadOnly (its BindableProperty has IsReadOnly=true).

Common situations: Coercing a read-only property from code that only holds the BindableProperty (not the key); generic coercion utilities that do not distinguish read-only properties.

Related errors


AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13). Data as JSON: /api/errors/33ad55fe109964c1. Report an issue: GitHub.