AvaloniaUI/Avalonia · error · ArgumentException
The pseudoclass '{name}' may only be {operation} by the cont
Error message
The pseudoclass '{name}' may only be {operation} by the control itself. What it means
Thrown by Classes.ThrowIfPseudoclass when an attempt is made to add or remove a pseudoclass (a class name starting with ':') through the public Classes collection. Pseudoclasses represent control visual states (e.g. ':pointerover', ':pressed') and are managed exclusively by the control itself via the protected StyledElement.PseudoClasses accessor, never by external code.
Source
Thrown at src/Avalonia.Base/Controls/Classes.cs:327
internal void RemoveListener(IClassesChangedListener listener)
{
_listeners?.Remove(listener);
}
private void NotifyChanged()
{
if (_listeners is null)
return;
foreach (var listener in _listeners)
listener.Changed();
}
private static void ThrowIfPseudoclass(string name, string operation)
{
if (name.StartsWith(":"))
{
throw new ArgumentException(
$"The pseudoclass '{name}' may only be {operation} by the control itself.");
}
}
/// <summary>
/// Adds a or removes a style class to/from the collection.
/// </summary>
/// <param name="name">The class names.</param>
/// <param name="value">If true adds the class, if false, removes it.</param>
/// <remarks>
/// Only standard classes may be added or removed via this method. To add pseudoclasses (classes
/// beginning with a ':' character) use the protected <see cref="StyledElement.PseudoClasses"/>
/// property.
/// </remarks>
public void Set(string name, bool value)
{
if (value)
{View on GitHub (pinned to 11c5427268)
Solutions
- If the class name should not start with ':', strip the colon before passing to Classes.Add/Remove.
- If you genuinely need to set a pseudoclass, call the protected PseudoClasses.Set(":name", value) from within the control (or a custom control subclass), not the public Classes API.
- If toggling a state from outside the control, expose a public property/styleable property on the control that internally sets PseudoClasses.
Example fix
// before
myControl.Classes.Add(":pressed");
// after — from inside the control or a subclass
PseudoClasses.Set(":pressed", true); Defensive patterns
Strategy: validation
Validate before calling
static bool IsPseudoclass(string name) => name.StartsWith(":");
// before mutating Classes:
if (!IsPseudoclass(name))
control.Classes.Add(name); Try / catch
try { control.Classes.Add(name); }
catch (ArgumentException ex) when (ex.Message.Contains("pseudoclass"))
{
// route to PseudoClasses.Set from within the control instead
} Prevention
- Never pass names starting with ':' to the public Classes API.
- Centralize class-name construction so the ':' prefix is only ever applied inside the control via PseudoClasses.
- Add a unit test asserting external APIs reject pseudoclass names.
When it happens
Trigger: Calling control.Classes.Add(":pressed"), control.Classes.Remove(":pointerover"), or any Classes collection mutation (Add/Remove/Insert) where the name argument starts with ':'. The guard fires in 9 call sites across Classes.cs (lines 66, 90, 130, 155, 183, 209, 235, 261).
Common situations: Developer confuses style classes (e.g. "active") with pseudoclasses (":active") and tries to toggle a pseudoclass from a view model or behavior. Migrating from WPF where pseudo-classes were sometimes manipulated differently. Dynamically building class names and accidentally prepending ':'.
Related errors
- NameScope is completed, no further registrations are allowed
- Control with the name '{name}' already registered.
- Expected control '{name}' to be '{typeof(T)} but it was '{re
- The control doesn't have an associated name scope, probably
AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13).
Data as JSON: /api/errors/775246da0d8d3111.
Report an issue: GitHub.