{"record":{"id":"2f5315c962afc6f6","repo":"Humanizr/Humanizer","slug":"phrase-section-path-must-be-a-mapping","errorCode":null,"errorMessage":"Phrase section '{path}' must be a mapping.","messagePattern":"Phrase section '(.+?)' must be a mapping\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs","lineNumber":544,"sourceCode":"                {\n                    throw new InvalidOperationException(\n                        allowedPlaceholders.Length == 0\n                            ? $\"Phrase '{path}' can only use placeholders in explicit 'template' entries.\"\n                            : $\"Phrase '{path}' can only use placeholders: {string.Join(\", \", allowedPlaceholders.Select(static value => $\"{{{value}}}\"))}.\");\n                }\n            }\n\n            return text;\n        }\n\n        static IEnumerable<string> GetPlaceholderNames(string text) =>\n            PlaceholderRegex.Matches(text)\n                .Cast<Match>()\n                .Select(static match => match.Groups[\"name\"].Value);\n\n        static SimpleYamlMapping ExpectMapping(SimpleYamlValue value, string path) =>\n            value as SimpleYamlMapping ??\n            throw new InvalidOperationException($\"Phrase section '{path}' must be a mapping.\");\n\n        static void RejectUnknownKeys(SimpleYamlMapping mapping, string path, params string[] allowedKeys)\n        {\n            foreach (var key in mapping.Values.Keys.Where(key => !allowedKeys.Contains(key, StringComparer.Ordinal)))\n            {\n                throw new InvalidOperationException(\n                    $\"Phrase section '{path}' defines unsupported property '{key}'. Supported properties: {string.Join(\", \", allowedKeys)}.\");\n            }\n        }\n    }\n}","sourceCodeStart":526,"sourceCodeEnd":555,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/LocalePhraseNormalization.cs#L526-L555","documentation":"Thrown by ExpectMapping when a YAML node that must be a mapping (object) is instead a scalar or sequence. Many phrase sections (relativeDate, duration, dataUnits, timeUnits, forms, template blocks) require mappings. Surfaced as compiler diagnostic HSG003 (severity Error).","triggerScenarios":"Writing 'duration: hours' (scalar) where a mapping of unit keys is expected, or 'forms: [a, b]' (sequence) where a mapping of form keys is expected.","commonSituations":"Author oversimplifies a section into a scalar; misreads the schema; YAML flow-style mistake producing a sequence; indentation turning a mapping into a scalar sibling.","solutions":["Restructure the node as a mapping with the expected keys (see the path in the message).","If a scalar is genuinely intended, move it to the field that accepts scalars (e.g. a unit value rather than the duration section).","Rebuild to confirm the diagnostic clears."],"exampleFix":"# before\nduration: hours\n# after\nduration:\n  hour:\n    forms:\n      default: hours","handlingStrategy":"validation","validationCode":"# Sections that must be mappings: relativeDate, duration, dataUnits, timeUnits,\n# past, future, forms, and any template block. Confirm their YAML tag is !!map.\nyq -o=json '[.phrases.relativeDate, .phrases.duration, .phrases.dataUnits, .phrases.timeUnits] | map(select(. != null and tag != \"!!map\"))' src/Humanizer/Locales/<code>.yml","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always follow a section key with nested keys on the next, deeper indentation line.","Avoid flow-style scalars/lists for mapping sections.","Build after structural changes."],"tags":["humanizer","source-generator","locale","yaml","localization","phrases","mapping","schema"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}