{"record":{"id":"d8defc8ea02389bb","repo":"Humanizr/Humanizer","slug":"phrase-path-must-define-forms-symbol-or-t","errorCode":null,"errorMessage":"Phrase '{path}' must define forms, 'symbol', or 'template'.","messagePattern":"Phrase '(.+?)' must define forms, 'symbol', or 'template'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs","lineNumber":226,"sourceCode":"\n        static DataUnitPhrase ParseDataUnitPhrase(SimpleYamlValue value, string path)\n        {\n            if (value is SimpleYamlScalar scalar)\n            {\n                return new DataUnitPhrase(PhraseForms.FromScalar(ValidateLiteralText(path, scalar.Value)));\n            }\n\n            var mapping = ExpectMapping(value, path);\n            RejectUnknownKeys(mapping, path, [\"forms\", \"default\", \"zero\", \"singular\", \"dual\", \"paucal\", \"plural\", \"many\", \"symbol\", \"template\"]);\n\n            var forms = ParseOptionalPhraseForms(mapping, path);\n            var symbol = GetOptionalLiteral(mapping, \"symbol\", $\"{path}.symbol\");\n            var template = mapping.TryGetValue(\"template\", out var templateValue)\n                ? ParseNamedTemplate(templateValue, $\"{path}.template\", [\"count\", \"unit\"])\n                : null;\n            if (forms is null && symbol is null && template is null)\n            {\n                throw new InvalidOperationException($\"Phrase '{path}' must define forms, 'symbol', or 'template'.\");\n            }\n\n            return new DataUnitPhrase(forms, symbol, template);\n        }\n\n        static TimeUnitPhraseSet ParseTimeUnit(string localeCode, SimpleYamlValue value, string path)\n        {\n            var mapping = ExpectMapping(value, path);\n            var builder = ImmutableDictionary.CreateBuilder<string, TimeUnitPhrase>(StringComparer.Ordinal);\n\n            foreach (var entry in mapping.Values)\n            {\n                builder[entry.Key] = ParseTimeUnitPhrase(entry.Value, $\"{path}.{entry.Key}\");\n            }\n\n            return new TimeUnitPhraseSet(builder.ToImmutable());\n        }\n","sourceCodeStart":208,"sourceCodeEnd":244,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs#L208-L244","documentation":"Thrown by the Humanizer source generator while parsing a locale's phrases.dataUnits.<unit> entry in src/Humanizer/Locales/*.yml. A data-unit phrase mapping must provide at least one of 'forms', 'symbol', or 'template'; if all three are absent the generator cannot emit a usable phrase and aborts that locale. The exception is caught in LocaleYamlCatalog and surfaced as compiler diagnostic HSG003 'Invalid locale definition' (severity Error) at build time.","triggerScenarios":"Authoring phrases.dataUnits.<unit> as a mapping that omits forms, symbol, and template (e.g. an empty block, or a block holding only countPlacement/unknown keys). A null-valued 'symbol:' (unquoted null) also counts as absent because GetOptionalLiteral returns null for it.","commonSituations":"Copying a sibling unit block and deleting its forms/symbol lines without replacing them; writing 'symbol:' with no value; editing a locale file and leaving a stub unit entry while translating.","solutions":["Open the locale YAML at the path in the message and add at least one of 'forms' (default/singular/plural/...), 'symbol', or 'template' under the offending dataUnits.<unit> entry.","If the unit should be a plain string, replace the mapping with a scalar value (e.g. 'byte: Bytes').","If the entry is unwanted, delete the unit key from dataUnits entirely.","Rebuild; the HSG003 diagnostic for that locale disappears when the phrase is well-formed."],"exampleFix":"# before\ndataUnits:\n  byte: {}\n# after\ndataUnits:\n  byte:\n    forms:\n      singular: byte\n      plural: bytes","handlingStrategy":"validation","validationCode":"# Before authoring, confirm each dataUnits unit has forms, symbol, or template.\n# Quick YAML lint (requires yq):\n#   yq -o=json '.phrases.dataUnits | to_entries | map(select((.value.forms // .value.symbol // .value.template // .value) == null))' src/Humanizer/Locales/<code>.yml\n# Any non-empty output is a unit that will trigger this error.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When stubbing a new data unit, write the forms/symbol block in the same edit rather than leaving an empty mapping.","Keep a sibling fully-authored unit beside the cursor as the copy source.","Run the build after each locale edit so HSG003 points at the exact path."],"tags":["humanizer","source-generator","locale","yaml","localization","phrases","data-units"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}