Humanizr/Humanizer · error · InvalidOperationException

Legacy locale '{localeCode}' defines unsupported top-level p

Error message

Legacy locale '{localeCode}' defines unsupported top-level property '{property}'.

What it means

Thrown by LegacyLocaleMigration.ConvertToCanonicalYaml when a legacy locale file contains a top-level YAML property that is not in the LegacyTopLevelNames whitelist (inherits, collectionFormatter, dateOnlyToOrdinalWords, dateToOrdinalWords, durationCases, formatter, grammar, headings, numberToWords, ordinalizer, phrases, timeOnlyToClockNotation, wordsToNumber). The migration tool only knows how to translate these specific legacy properties into canonical surfaces.

Source

Thrown at src/Humanizer.SourceGenerators/Common/CanonicalLocaleAuthoring.cs:658

            "dateOnlyToOrdinalWords",
            "dateToOrdinalWords",
            "durationCases",
            "formatter",
            "grammar",
            "headings",
            "numberToWords",
            "ordinalizer",
            "phrases",
            "timeOnlyToClockNotation",
            "wordsToNumber"
        ];

        public static string ConvertToCanonicalYaml(string localeCode, string fileText)
        {
            var root = SimpleYamlParser.Parse(fileText);
            foreach (var property in root.Values.Keys.Where(static property => !LegacyTopLevelNames.Contains(property, StringComparer.Ordinal)))
            {
                throw new InvalidOperationException(
                    $"Legacy locale '{localeCode}' defines unsupported top-level property '{property}'.");
            }

            var builder = new StringBuilder();
            builder.Append("locale: ");
            builder.AppendLine(QuoteScalar(new SimpleYamlScalar(localeCode, true)));

            if (root.GetScalar("inherits") is { } variantOf)
            {
                builder.Append("variantOf: ");
                builder.AppendLine(QuoteScalar(new SimpleYamlScalar(variantOf, true)));
            }

            var hasInherits = root.GetScalar("inherits") is not null;
            var hasSurfaces = root.Values.Keys.Any(static key => key != "inherits");
            if (!hasSurfaces)
            {
                if (hasInherits)

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Remove or rename the unsupported top-level property so only whitelisted legacy names remain.
  2. If the property is already canonical (e.g. 'surfaces'), migrate the entire file to canonical format instead of using ConvertToCanonicalYaml.
  3. Check the LegacyTopLevelNames array in CanonicalLocaleAuthoring.cs:636 for the exact list of accepted property names.

Example fix

# before (legacy file with unsupported key)
ordinal:
  engine: default
inherits: en
# after (remove non-legacy key; or convert fully to canonical)
inherits: en
Defensive patterns

Strategy: validation

Validate before calling

static readonly HashSet<string> LegacyTopLevelNames = new(StringComparer.Ordinal)
{
    "inherits", "collectionFormatter", "dateOnlyToOrdinalWords", "dateToOrdinalWords",
    "durationCases", "formatter", "grammar", "headings", "numberToWords",
    "ordinalizer", "phrases", "timeOnlyToClockNotation", "wordsToNumber"
};

static string? FindUnsupportedLegacyKey(SimpleYamlMapping root) =>
    root.Values.Keys.FirstOrDefault(k => !LegacyTopLevelNames.Contains(k));

Try / catch

// Wrap ConvertToCanonicalYaml for safe migration with diagnostics
try
{
    var canonical = LegacyLocaleMigration.ConvertToCanonicalYaml(localeCode, fileText);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("unsupported top-level property"))
{
    logger.LogWarning("Locale {Locale} has unsupported legacy properties; skipping migration", localeCode);
    throw;
}

Prevention

When it happens

Trigger: ConvertToCanonicalYaml parses the legacy file, iterates root.Values.Keys, and throws for any key not in LegacyTopLevelNames. For example, a legacy file with a top-level 'ordinal:' or 'clock:' key (which are canonical surface names, not legacy names) would fail.

Common situations: A contributor runs the legacy migration on a file that was already partially converted to canonical format, mixing canonical and legacy keys. Or a custom/typo property name is present at the root level. Also happens when a locale file uses an older schema with properties the migration tool doesn't recognize.

Related errors


AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13). Data as JSON: /api/errors/d50d530f53a474cd. Report an issue: GitHub.