AvaloniaUI/Avalonia · error · InvalidOperationException

ControlTheme style may not directly contain a child or desce

Error message

ControlTheme style may not directly contain a child or descendent selector.

What it means

Thrown during ValidateNestingSelector when a selector chain belonging to a style nested in a ControlTheme contains a child (`>`) or descendant combinator outside of a `:template()` scope. ControlThemes target a single control instance, so relating to children/descendants of that control is only meaningful after entering the control's template via the Template selector.

Source

Thrown at src/Avalonia.Base/Styling/Selector.cs:118

        /// <summary>
        /// Moves to the previous selector.
        /// </summary>
        private protected abstract Selector? MovePrevious();

        /// <summary>
        /// Moves to the previous selector or the parent selector.
        /// </summary>
        private protected abstract Selector? MovePreviousOrParent();

        internal virtual void ValidateNestingSelector(bool inControlTheme, int templateCount = 0)
        {
            var s = this;

            if (inControlTheme)
            {
                if (!s.InTemplate && s.IsCombinator)
                    throw new InvalidOperationException(
                        "ControlTheme style may not directly contain a child or descendent selector.");
                if (s is TemplateSelector && templateCount++ > 0)
                    throw new InvalidOperationException(
                        "ControlTemplate styles cannot contain multiple template selectors.");
            }

            var previous = s.MovePreviousOrParent();

            if (previous is null)
            {
                if (s is not NestingSelector)
                    throw new InvalidOperationException("Child styles must have a nesting selector.");
            }
            else
            {
                previous.ValidateNestingSelector(inControlTheme, templateCount);
            }
        }

View on GitHub (pinned to 11c5427268)

Solutions

  1. Add a Template selector step before the combinator, e.g. `Button /template/ ContentPresenter > TextBlock`.
  2. Move child/descendant rules that target arbitrary visual-tree nodes out of the ControlTheme into an application-level Style.
  3. Re-read the selector chain and ensure every combinator is preceded by `:template()` when inside a ControlTheme.

Example fix

// before (inside ControlTheme, invalid)
new Style(s => s.OfType<Button>().Child().OfType<ContentPresenter>())

// after (enter template first)
new Style(s => s.OfType<Button>().Template().OfType<ContentPresenter>())
Defensive patterns

Strategy: validation

Validate before calling

// Ensure every combinator in a ControlTheme selector is preceded by a Template step.
static bool SelectorValidForTheme(Selector s)
{
    // Walk the chain; combinators are only allowed after InTemplate becomes true.
    return true; // implement per-chain walk using InTemplate/IsCombinator

Prevention

When it happens

Trigger: Defining a ControlTheme child style with a selector like `Button > ContentPresenter` or `^ TextBlock` (descendant) without first traversing `:template()`. The check fires when `!InTemplate && IsCombinator` while inControlTheme is true.

Common situations: Writing theme styles that try to reach into the visual tree the way application-level Styles do; forgetting to add `/template/ ` before addressing template parts; converting an app Style to a ControlTheme without adjusting combinators.

Related errors


AI-assisted analysis of AvaloniaUI/Avalonia@11c5427268 (2026-08-13). Data as JSON: /api/errors/5ef94081c5c1ec8d. Report an issue: GitHub.