AvaloniaUI/Avalonia · error · InvalidOperationException

'{FontFamily.DefaultFontFamilyName}' is a placeholder and ca

Error message

'{FontFamily.DefaultFontFamilyName}' is a placeholder and cannot be used as the default font family name. Provide a concrete font family name via {nameof(FontManagerOptions)} or the platform implementation.

What it means

GetDefaultFontFamilyName throws InvalidOperationException when the resolved default font family name equals the placeholder constant FontFamily.DefaultFontFamilyName (which is "$Default"). "$Default" is a sentinel meaning 'use the system default'; using it as the concrete default would cause infinite recursion/unresolved resolution, so it is explicitly rejected. A real font name must be supplied via FontManagerOptions or the platform.

Source

Thrown at src/Avalonia.Base/Media/FontManager.cs:456

        private string GetDefaultFontFamilyName(FontManagerOptions? options)
        {
            var defaultFontFamilyName = options?.DefaultFamilyName
                ?? PlatformImpl.GetDefaultFontFamilyName();

            if (string.IsNullOrEmpty(defaultFontFamilyName) && SystemFonts.Count > 0)
            {
                defaultFontFamilyName = SystemFonts[0].Name;
            }

            if (string.IsNullOrEmpty(defaultFontFamilyName))
            {
                throw new InvalidOperationException(
                    "Default font family name can't be null or empty.");
            }

            if (defaultFontFamilyName == FontFamily.DefaultFontFamilyName)
            {
                throw new InvalidOperationException(
                    $"'{FontFamily.DefaultFontFamilyName}' is a placeholder and cannot be used as the default font family name. Provide a concrete font family name via {nameof(FontManagerOptions)} or the platform implementation.");
            }

            return defaultFontFamilyName;
        }

        void IDisposable.Dispose()
        {
            foreach (var pair in _fontCollections)
                pair.Value.Dispose();

            _fontCollections.Clear();
            (PlatformImpl as IDisposable)?.Dispose();
        }
    }
}

View on GitHub (pinned to 11c5427268)

Solutions

  1. Set FontManagerOptions.DefaultFamilyName to a concrete, installed font family name (e.g. "Segoe UI", "Inter", "DejaVu Sans").
  2. Leave DefaultFamilyName null so the platform impl and SystemFonts fallback resolve a real font.
  3. Never use "$Default" (FontFamily.DefaultFontFamilyName) as the options value.

Example fix

// before
services.AddSingleton<FontManagerOptions>(_ => new FontManagerOptions
{
    DefaultFamilyName = FontFamily.DefaultFontFamilyName // "$Default"
});

// after
services.AddSingleton<FontManagerOptions>(_ => new FontManagerOptions
{
    DefaultFamilyName = "Inter"
});
Defensive patterns

Strategy: validation

Validate before calling

if (defaultFamilyName == FontFamily.DefaultFontFamilyName) // "$Default"
    defaultFamilyName = "Inter"; // concrete fallback
var opts = new FontManagerOptions { DefaultFamilyName = defaultFamilyName };

Prevention

When it happens

Trigger: FontManagerOptions.DefaultFamilyName (or the platform implementation) returns the literal string "$Default". This happens when config or a binding forwards the sentinel placeholder instead of a concrete family name.

Common situations: Setting DefaultFamilyName = FontFamily.DefaultFontFamilyName in options (thinking it means 'default'). A platform impl incorrectly returning the sentinel. Copying a XAML/theme value that used the placeholder into the options.

Related errors


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