{"record":{"id":"72a2b544ee6128ad","repo":"Humanizr/Humanizer","slug":"path-units-must-explicitly-define-unitname","errorCode":null,"errorMessage":"'{path}.units' must explicitly define '{unitName}'.","messagePattern":"'(.+?)\\.units' must explicitly define '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs","lineNumber":1137,"sourceCode":"\n        static DurationCaseOverlay ParseCase(SimpleYamlValue value, string path)\n        {\n            var mapping = ExpectMapping(value, path);\n            RejectUnknownKeys(mapping, path, [\"units\"]);\n\n            if (!mapping.TryGetValue(\"units\", out var unitsValue))\n            {\n                throw new InvalidOperationException($\"'{path}' must define all duration 'units'.\");\n            }\n\n            var unitsMapping = ExpectMapping(unitsValue, $\"{path}.units\");\n            RejectUnknownKeys(unitsMapping, $\"{path}.units\", TimeUnits);\n            var units = ImmutableDictionary.CreateBuilder<string, DurationCaseUnit>(StringComparer.Ordinal);\n            foreach (var unitName in TimeUnits)\n            {\n                if (!unitsMapping.TryGetValue(unitName, out var unitValue))\n                {\n                    throw new InvalidOperationException(\n                        $\"'{path}.units' must explicitly define '{unitName}'.\");\n                }\n\n                units[unitName] = ParseUnit(unitValue, $\"{path}.units.{unitName}\");\n            }\n\n            return new DurationCaseOverlay(units.ToImmutable());\n        }\n\n        static DurationCaseUnit ParseUnit(SimpleYamlValue value, string path)\n        {\n            if (ContainsInheritanceMarker(value))\n            {\n                throw new InvalidOperationException(\n                    $\"'{path}' contains an unresolved CLDR inheritance marker.\");\n            }\n\n            var mapping = ExpectMapping(value, path);","sourceCodeStart":1119,"sourceCodeEnd":1155,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/DurationCaseModels.cs#L1119-L1155","documentation":"Thrown by the source generator's DurationCaseNormalization.ParseCase while parsing a duration-case overlay (e.g. the 'genitive' case) in a locale's durationCases YAML. The generator iterates the fixed TimeUnits list (millisecond, second, minute, hour, day, week, month, year) and requires every unit to appear under units:, so emitted code never references a missing case form. The {path} is '<locale>.durationCases.cases.<case>.units' and {unitName} is the missing unit key.","triggerScenarios":"A durationCases.cases.<case>.units mapping omits one of the eight fixed time-unit keys (most commonly 'millisecond' or 'year'). ParseCase (DurationCaseModels.cs:1133-1139) fails the TryGetValue for that unitName.","commonSituations":"A contributor adds a new grammatical case to a Slavic locale and forgets the rarely-used 'millisecond' unit; partial copy-paste from another locale; renaming a unit key while the generator still expects the canonical name.","solutions":["Add the missing unit key (named exactly, e.g. 'millisecond:') under the case's units: mapping.","If the unit has no distinct form in this case, set it to 'sameAsNominative: true' or 'unsupported: true' instead of authoring a phrase.","Cross-check the unit list against the locale's nominative block and the TimeUnits array in DurationCaseModels.cs:243.","Rebuild and run the locale's tests under tests/Humanizer.Tests/Localisation/<culture>."],"exampleFix":"# before\ncases:\n  genitive:\n    units:\n      second:\n        phrase: { ... }\n      minute:\n        phrase: { ... }\n# after - millisecond added\ncases:\n  genitive:\n    units:\n      millisecond:\n        sameAsNominative: true\n      second:\n        phrase: { ... }\n      minute:\n        phrase: { ... }","handlingStrategy":"validation","validationCode":"# Pre-build check over a parsed duration-case overlay (PowerShell + YamlDotNet pattern)\n$required = 'millisecond','second','minute','hour','day','week','month','year'\n$caseUnits = $parsed.durationCases.cases.$case.units   # the mapping under test\n$missing = $required | Where-Object { -not $caseUnits.ContainsKey($_) }\nif ($missing) { throw \"case '$case' missing units: $($missing -join ', ')\" }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Author new cases by copying a complete nominative block, then editing, so all eight units are present.","Keep the TimeUnits list (DurationCaseModels.cs:243) open when adding cases.","Run the locale's tests under tests/Humanizer.Tests/Localisation/<culture> before pushing."],"tags":["source-generator","build-time","localization","yaml","duration-cases","locale-data"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}