stride3d/stride · error · InvalidOperationException

The specified DefaultCharacter is not part of this font.

Error message

The specified DefaultCharacter is not part of this font.

What it means

Like the offline compiler, SignedDistanceFieldFontCompiler.ImportFont requires the asset's DefaultCharacter to be among the imported glyphs. If DefaultCharacterExists fails after the glyph list is built and sorted, it throws this InvalidOperationException so runtime fallback rendering never resolves to a missing glyph.

Solutions

  1. Change DefaultCharacter to a character guaranteed to be in the font and included in the CharacterSet (e.g. '?').
  2. Add the desired default character to the CharacterSet file so it gets imported.
  3. Set DefaultCharacter to 0/null if no fallback glyph is required.
  4. Intersect the CharacterSet with the font's cmap (font inspection tool) to confirm coverage before building.

Example fix

// before (font asset)
<DefaultCharacter>★</DefaultCharacter> <!-- not in charset -->
// after: add the char to charset.txt, or use
<DefaultCharacter>?</DefaultCharacter>
Defensive patterns

Strategy: validation

Validate before calling

var chars = SignedDistanceFieldFontCompiler.GetCharactersToImport(asset);
if (asset.DefaultCharacter != default && !chars.Contains(asset.DefaultCharacter.Value))
    asset.DefaultCharacter = '?'; // ensure the default is part of the imported set

Type guard

bool DefaultWillBeImported(SpriteFontAsset asset) =>
    asset.DefaultCharacter == default ||
    SignedDistanceFieldFontCompiler.GetCharactersToImport(asset).Contains(asset.DefaultCharacter.Value);

Try / catch

try
{
    var font = SignedDistanceFieldFontCompiler.Compile(factory, asset);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("DefaultCharacter"))
{
    asset.DefaultCharacter = '?';
    font = SignedDistanceFieldFontCompiler.Compile(factory, asset); // retry with safe default
}

Prevention

When it happens

Trigger: Importing an SDF font whose DefaultCharacter is set to a codepoint not present in the font or excluded from the CharacterSet used to build the glyph list.

Common situations: DefaultCharacter set to 'é' (or a CJK/emoji character) while the CharacterSet only lists ASCII; switching the font file without updating DefaultCharacter; default character left over from a different asset template.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/0f11f9cfa4d3dd6c. Report an issue: GitHub.

Appendix: source

Thrown at sources/engine/Stride.Assets/SpriteFont/Compiler/SignedDistanceFieldFontCompiler.cs:158

            lineSpacing = importer.LineSpacing;
            baseLine = importer.BaseLine;

            // Get all glyphs
            var glyphs = new List<Glyph>(importer.Glyphs);

            // Validate.
            if (glyphs.Count == 0)
            {
                throw new Exception("Font does not contain any glyphs.");
            }

            // Sort the glyphs
            glyphs.Sort((left, right) => left.Character.CompareTo(right.Character));

            // Check that the default character is part of the glyphs
            if (!DefaultCharacterExists(options.DefaultCharacter, glyphs))
            {
                throw new InvalidOperationException("The specified DefaultCharacter is not part of this font.");
            }

            return glyphs.ToArray();
        }

        private static bool DefaultCharacterExists(char defaultCharacter, List<Glyph> glyphs)
        {
            if (defaultCharacter == 0)
                return true;

            foreach (var glyph in glyphs)
            {
                if (glyph.Character == defaultCharacter)
                    return true;
            }

            return false;
        }

View on GitHub (pinned to 96fad776d2)