Humanizr/Humanizer · error · InvalidOperationException

Unsupported conjunctional-scale strategy.

Error message

Unsupported conjunctional-scale strategy.

What it means

The ShouldAddAnd switch over profile.AndStrategy hit its default arm, meaning the profile declared a ConjunctionalScaleAndStrategy enum value the converter does not recognize. This is a locale-profile / source-generator defect, not bad user input: the YAML/generated profile named a conjunction strategy with no matching case branch. It fires on any cardinal or ordinal conversion for a ConjunctionalScale locale (English-family: en, en-GB, en-IN, etc.) when addAnd is true.

Source

Thrown at src/Humanizer/Localisation/NumberToWords/ConjunctionalScaleNumberToWordsConverter.cs:221

        return $"{profile.OrdinalWholePhrasePrefix}{ConvertCore(number, isOrdinal: false, addAnd)}";
    }

    string GetUnitValue(long number, bool isOrdinal) =>
        isOrdinal ? profile.OrdinalUnitsMap[number] : profile.UnitsMap[number];

    // "and" placement is modeled as a structural rule family rather than as locale branches.
    // The generated profile says which insertion points are legal, and this method translates that
    // choice into the current local context: inside a hundred group, after a higher scale, both, or neither.
    bool ShouldAddAnd(bool addAnd, bool hasHundreds, bool hasScalePrefix, bool isTerminalScaleRemainder) =>
        addAnd && profile.AndStrategy switch
        {
            ConjunctionalScaleAndStrategy.WithinGroupAndAfterScaleSubHundredRemainder => hasHundreds || hasScalePrefix,
            ConjunctionalScaleAndStrategy.WithinGroupOnly => hasHundreds,
            ConjunctionalScaleAndStrategy.AfterScaleSubHundredRemainderOnly => !hasHundreds && hasScalePrefix,
            ConjunctionalScaleAndStrategy.WithinGroupAndTerminalScaleSubHundredRemainder => hasHundreds || (hasScalePrefix && isTerminalScaleRemainder),
            ConjunctionalScaleAndStrategy.Never => false,
            _ => throw new InvalidOperationException("Unsupported conjunctional-scale strategy.")
        };

    void ApplyOrdinalLeadingOneStrategy(List<string> parts, bool isOrdinal)
    {
        if (!isOrdinal || profile.OrdinalLeadingOneStrategy != ConjunctionalScaleOrdinalLeadingOneStrategy.OmitLeadingOne || parts.Count == 0)
        {
            return;
        }

        if (parts[0] == profile.UnitsMap[1])
        {
            parts.RemoveAt(0);
            return;
        }

        if (parts[0].StartsWith(profile.UnitsMap[1] + " ", StringComparison.Ordinal))
        {
            parts[0] = parts[0][profile.UnitsMap[1].Length..].TrimStart();

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Report the locale + Humanizer version — this is a library/locale-data bug requiring a switch case or corrected profile data.
  2. Use a consistent Humanizer version where the profile data and the switch agree.
  3. If authoring a locale, set AndStrategy to one of WithinGroupAndAfterScaleSubHundredRemainder, WithinGroupOnly, AfterScaleSubHundredRemainderOnly, WithinGroupAndTerminalScaleSubHundredRemainder, or Never.
Defensive patterns

Strategy: try-catch

Validate before calling

// AndStrategy is internal profile data; callers cannot pre-validate it. The only signal is the
// InvalidOperationException at conversion time. Wrap converter resolution + Convert together.

Try / catch

string words;
try
{
    words = value.ToWords(culture);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("conjunctional-scale strategy"))
{
    // Profile/locale-data defect; fall back to a known-good culture.
    words = value.ToWords(CultureInfo.InvariantCulture);
}

Prevention

When it happens

Trigger: Any ToWords()/ToOrdinalWords() call (with addAnd true, the default) for a ConjunctionalScale locale whose profile.AndStrategy is an enum value outside the five handled cases.

Common situations: After a Humanizer version upgrade that introduced a new ConjunctionalScaleAndStrategy member without updating the switch; a hand-edited or stale generated profile carrying an out-of-range enum value.

Related errors


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