{"record":{"id":"eefb069405e0bb44","repo":"Humanizr/Humanizer","slug":"a-dedicated-71-spelling-is-required-when-specialse","errorCode":null,"errorMessage":"A dedicated 71 spelling is required when specialSeventyOneEnabled is true.","messagePattern":"A dedicated 71 spelling is required when specialSeventyOneEnabled is true\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"src/Humanizer/Localisation/NumberToWords/VariantDecadeNumberToWordsConverter.cs","lineNumber":289,"sourceCode":"    VariantDecadeSeventyStrategy seventyStrategy,\n    VariantDecadeNinetyStrategy ninetyStrategy,\n    bool specialSeventyOneEnabled,\n    string? specialSeventyOneWord,\n    bool pluralizeExactEighty,\n    FrozenSet<int> tensUsingEtWhenUnitIsOne,\n    string[] tensMap,\n    VariantDecadeScale[] scales)\n{\n    /// <summary>Gets the word used to prefix negative values.</summary>\n    public string MinusWord { get; } = minusWord;\n    /// <summary>Gets the strategy used for the seventies.</summary>\n    public VariantDecadeSeventyStrategy SeventyStrategy { get; } = seventyStrategy;\n    /// <summary>Gets the strategy used for the nineties.</summary>\n    public VariantDecadeNinetyStrategy NinetyStrategy { get; } = ninetyStrategy;\n    /// <summary>Gets a value indicating whether the locale uses a dedicated spelling for 71.</summary>\n    public bool SpecialSeventyOneEnabled { get; } = specialSeventyOneEnabled;\n    /// <summary>Gets the optional dedicated spelling for 71 when the locale needs one.</summary>\n    public string SpecialSeventyOneWord { get; } = specialSeventyOneWord ?? throw new InvalidOperationException(\"A dedicated 71 spelling is required when specialSeventyOneEnabled is true.\");\n    /// <summary>Gets a value indicating whether exact 80 takes a plural suffix.</summary>\n    public bool PluralizeExactEighty { get; } = pluralizeExactEighty;\n    /// <summary>Gets the set of tens indices that use an \"et un\" style joiner when the unit is one.</summary>\n    public FrozenSet<int> TensUsingEtWhenUnitIsOne { get; } = tensUsingEtWhenUnitIsOne;\n    /// <summary>Gets the tens lexicon.</summary>\n    public string[] TensMap { get; } = tensMap;\n    /// <summary>Gets the descending scale rows.</summary>\n    public VariantDecadeScale[] Scales { get; } = scales;\n}\n\nreadonly record struct VariantDecadeScale(\n    ulong Value,\n    string Singular,\n    string Plural,\n    bool OmitOne = false,\n    bool PluralizeMultiplier = true);","sourceCodeStart":271,"sourceCodeEnd":305,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer/Localisation/NumberToWords/VariantDecadeNumberToWordsConverter.cs#L271-L305","documentation":"This InvalidOperationException is thrown inside the VariantDecadeNumberToWordsProfile constructor (a primary-constructor field initializer) when specialSeventyOneEnabled is true but specialSeventyOneWord is null. It is a locale-authoring invariant: French-family locales that spell 71 as a dedicated word (e.g. 'soixante et onze') must supply that word in the profile. The exception fires during locale registry initialization, not during a normal Convert call, so it typically surfaces at first use of the affected culture or during application warm-up.","triggerScenarios":"Constructing or resolving a VariantDecadeNumberToWordsProfile whose YAML or generated registry data sets specialSeventyOneEnabled: true while leaving the special_seventy_one_word field empty or null. This happens when a locale file is hand-edited, a code generator produces an incomplete profile, or a locale fork omits the word.","commonSituations":"Editing a French-family locale YAML file and setting special_seventy_one_enabled without providing special_seventy_one_word; running a partial locale generator output; forking a locale and forgetting the 71 entry; upgrading Humanizer and hitting a regression in generated locale code.","solutions":["Provide a non-null specialSeventyOneWord in the locale profile data (YAML) wherever specialSeventyOneEnabled is true.","If the locale does not use a special 71 spelling, set specialSeventyOneEnabled to false and remove the word.","Regenerate the locale source from the YAML using the project's source generator so the profile and its fields stay consistent.","Revert any hand-edit that changed one field without the other."],"exampleFix":"// before (locale YAML fragment)\n//  special_seventy_one_enabled: true\n//  # special_seventy_one_word missing\n\n// after\n//  special_seventy_one_enabled: true\n//  special_seventy_one_word: \"soixante et onze\"","handlingStrategy":"validation","validationCode":"// For locale authors: verify profile consistency before registration\nbool IsProfileValid(bool enabled, string? word) =>\n    !enabled || !string.IsNullOrEmpty(word);","typeGuard":null,"tryCatchPattern":"try\n{\n    // First use of the affected culture triggers profile construction\n    return number.ToWords(new CultureInfo(\"fr-BE\"));\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"71 spelling\"))\n{\n    // Locale data is corrupt; fall back to a known-good culture\n    return number.ToWords(new CultureInfo(\"fr-FR\"));\n}","preventionTips":["Treat locale YAML edits as code changes: test them with a sample Convert call before shipping.","Keep specialSeventyOneEnabled and specialSeventyOneWord in sync — enabling one requires the other.","Run the project's locale tests after any YAML change to catch profile-construction errors at CI.","Regenerate locale source via the source generator after editing YAML rather than editing generated code by hand."],"tags":["invalidoperation","locale-data","variant-decade","profile-construction","configuration"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}