{"record":{"id":"03b6dd019ae87a1c","repo":"Humanizr/Humanizer","slug":"unsupported-conjunctional-scale-strategy","errorCode":null,"errorMessage":"Unsupported conjunctional-scale strategy.","messagePattern":"Unsupported conjunctional-scale strategy\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer/Localisation/NumberToWords/ConjunctionalScaleNumberToWordsConverter.cs","lineNumber":221,"sourceCode":"\n        return $\"{profile.OrdinalWholePhrasePrefix}{ConvertCore(number, isOrdinal: false, addAnd)}\";\n    }\n\n    string GetUnitValue(long number, bool isOrdinal) =>\n        isOrdinal ? profile.OrdinalUnitsMap[number] : profile.UnitsMap[number];\n\n    // \"and\" placement is modeled as a structural rule family rather than as locale branches.\n    // The generated profile says which insertion points are legal, and this method translates that\n    // choice into the current local context: inside a hundred group, after a higher scale, both, or neither.\n    bool ShouldAddAnd(bool addAnd, bool hasHundreds, bool hasScalePrefix, bool isTerminalScaleRemainder) =>\n        addAnd && profile.AndStrategy switch\n        {\n            ConjunctionalScaleAndStrategy.WithinGroupAndAfterScaleSubHundredRemainder => hasHundreds || hasScalePrefix,\n            ConjunctionalScaleAndStrategy.WithinGroupOnly => hasHundreds,\n            ConjunctionalScaleAndStrategy.AfterScaleSubHundredRemainderOnly => !hasHundreds && hasScalePrefix,\n            ConjunctionalScaleAndStrategy.WithinGroupAndTerminalScaleSubHundredRemainder => hasHundreds || (hasScalePrefix && isTerminalScaleRemainder),\n            ConjunctionalScaleAndStrategy.Never => false,\n            _ => throw new InvalidOperationException(\"Unsupported conjunctional-scale strategy.\")\n        };\n\n    void ApplyOrdinalLeadingOneStrategy(List<string> parts, bool isOrdinal)\n    {\n        if (!isOrdinal || profile.OrdinalLeadingOneStrategy != ConjunctionalScaleOrdinalLeadingOneStrategy.OmitLeadingOne || parts.Count == 0)\n        {\n            return;\n        }\n\n        if (parts[0] == profile.UnitsMap[1])\n        {\n            parts.RemoveAt(0);\n            return;\n        }\n\n        if (parts[0].StartsWith(profile.UnitsMap[1] + \" \", StringComparison.Ordinal))\n        {\n            parts[0] = parts[0][profile.UnitsMap[1].Length..].TrimStart();","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer/Localisation/NumberToWords/ConjunctionalScaleNumberToWordsConverter.cs#L203-L239","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Report the locale + Humanizer version — this is a library/locale-data bug requiring a switch case or corrected profile data.","Use a consistent Humanizer version where the profile data and the switch agree.","If authoring a locale, set AndStrategy to one of WithinGroupAndAfterScaleSubHundredRemainder, WithinGroupOnly, AfterScaleSubHundredRemainderOnly, WithinGroupAndTerminalScaleSubHundredRemainder, or Never."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// AndStrategy is internal profile data; callers cannot pre-validate it. The only signal is the\n// InvalidOperationException at conversion time. Wrap converter resolution + Convert together.","typeGuard":null,"tryCatchPattern":"string words;\ntry\n{\n    words = value.ToWords(culture);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"conjunctional-scale strategy\"))\n{\n    // Profile/locale-data defect; fall back to a known-good culture.\n    words = value.ToWords(CultureInfo.InvariantCulture);\n}","preventionTips":["Pin a Humanizer version where the locale profile and the switch agree.","Smoke-test ToWords for each locale you ship after any Humanizer upgrade.","Report locale-data defects upstream rather than compensating in application code."],"tags":["number-to-words","profile-data","internal-invariant"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}