dotnet/maui · error · NotSupportedException
DataTemplateSelector.OnSelectTemplate must not return…
Error message
DataTemplateSelector.OnSelectTemplate must not return another DataTemplateSelector
What it means
DataTemplateSelector.SelectTemplate calls the overridden OnSelectTemplate and then rejects the result if it is itself a DataTemplateSelector (NotSupportedException). Nested selectors are forbidden because SelectTemplate caches per-type templates and the recycling path assumes a concrete DataTemplate; allowing a selector to return a selector would create unbounded recursion and break caching.
Solutions
- Make OnSelectTemplate return a concrete DataTemplate (or null), never a DataTemplateSelector.
- If you want composition, call the inner selector's SelectTemplate (or OnSelectTemplate) inside your override and return the resolved DataTemplate it yields.
- Cache concrete DataTemplate instances as fields and return those.
Example fix
// before
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
=> item is TypeA ? _otherSelector : _templateB; // _otherSelector is a DataTemplateSelector -> throws
// after
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
=> item is TypeA ? _otherSelector.SelectTemplate(item, container) : _templateB; Defensive patterns
Strategy: validation
Validate before calling
// Guard OnSelectTemplate implementations to never return a selector.
static DataTemplate SafeSelect(DataTemplate result)
{
if (result is DataTemplateSelector)
throw new ArgumentException("OnSelectTemplate returned a DataTemplateSelector; resolve to a concrete DataTemplate first.");
return result;
} Type guard
static bool IsConcreteDataTemplate(DataTemplate t) => t is not DataTemplateSelector;
Prevention
- Make SelectTemplate/OnSelectTemplate return cached concrete DataTemplate fields.
- When composing selectors, call SelectTemplate on the inner selector and return its resolved template.
- Add a unit test asserting every branch of OnSelectTemplate returns a non-selector DataTemplate.
When it happens
Trigger: A custom DataTemplateSelector whose OnSelectTemplate returns an instance of another DataTemplateSelector subclass; delegating selection to a composed selector and returning that selector object rather than its resolved DataTemplate.
Common situations: Refactoring a single large selector into multiple selectors and accidentally returning the sub-selector; intent to compose selection logic by chaining selectors.
Related errors
- ItemTemplate count has exceeded the limit of
- RecycleElementAndDataTemplate requires DataTemplate…
- ItemTemplate count has exceeded the limit of
- ItemTemplate count has exceeded the limit of
- The Map.ItemTemplate property only supports DataTemplate…
AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13).
Data as JSON: /api/errors/119bfdb7b3171970.
Report an issue: GitHub.
Appendix: source
Thrown at src/Controls/src/Core/DataTemplateSelector.cs:34
/// <param name="container">The bindable object that will display the templated item.</param>
/// <returns>The selected <see cref="DataTemplate"/>.</returns>
public DataTemplate SelectTemplate(object item, BindableObject container)
{
#pragma warning disable CS0618 // Type or member is obsolete
var listView = container as ListView;
#pragma warning restore CS0618 // Type or member is obsolete
var recycle = listView == null ? false :
(listView.CachingStrategy & ListViewCachingStrategy.RecycleElementAndDataTemplate) ==
ListViewCachingStrategy.RecycleElementAndDataTemplate;
DataTemplate dataTemplate = null;
if (recycle && _dataTemplates.TryGetValue(item.GetType(), out dataTemplate))
return dataTemplate;
dataTemplate = OnSelectTemplate(item, container);
if (dataTemplate is DataTemplateSelector)
throw new NotSupportedException(
"DataTemplateSelector.OnSelectTemplate must not return another DataTemplateSelector");
if (recycle)
{
if (!dataTemplate.CanRecycle)
throw new NotSupportedException(
"RecycleElementAndDataTemplate requires DataTemplate activated with ctor taking a type.");
_dataTemplates[item.GetType()] = dataTemplate;
}
return dataTemplate;
}
protected abstract DataTemplate OnSelectTemplate(object item, BindableObject container);
}
}View on GitHub (pinned to f377ff1c5e)