{"record":{"id":"b0d19573642f0a20","repo":"Humanizr/Humanizer","slug":"inflection-rule-ruleid-output-must-contain-exa","errorCode":null,"errorMessage":"Inflection rule '{ruleId}' output must contain exactly one bounded '{stem}' placeholder.","messagePattern":"Inflection rule '(.+?)' output must contain exactly one bounded '(.+?)' placeholder\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogValidation.cs","lineNumber":146,"sourceCode":"                         string.IsNullOrWhiteSpace(optionalScalar.Value)))\n                    {\n                        throw new InvalidOperationException(\n                            $\"Inflection source '{entry.Key}' {optionalProperty} must be a non-empty scalar.\");\n                    }\n                }\n            }\n        }\n\n        static void ValidateTemplate(string ruleId, string template)\n        {\n            var marker = template.IndexOf(\"{stem}\", StringComparison.Ordinal);\n            var remainder = template.Replace(\"{stem}\", string.Empty);\n            if (marker < 0 ||\n                template.IndexOf(\"{stem}\", marker + \"{stem}\".Length, StringComparison.Ordinal) >= 0 ||\n                remainder.Contains('{') ||\n                remainder.Contains('}'))\n            {\n                throw new InvalidOperationException(\n                    $\"Inflection rule '{ruleId}' output must contain exactly one bounded '{{stem}}' placeholder.\");\n            }\n        }\n\n        static void ValidateInvariantCapability(\n            string capability,\n            string casing,\n            ImmutableArray<InflectionLexemeInput> lexemes,\n            ImmutableArray<InflectionRuleInput> rules)\n        {\n            if (capability != \"invariant\")\n            {\n                return;\n            }\n\n            if (!rules.IsEmpty)\n            {\n                throw new InvalidOperationException(","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/InflectionCatalogValidation.cs#L128-L164","documentation":"Every inflection rule output template must contain exactly one '{stem}' placeholder, bounded by curly braces, with no additional unbounded braces elsewhere. The generator uses this placeholder to splice the lexeme stem into the inflected form at code-generation time, so the template grammar must be unambiguous.","triggerScenarios":"ValidateTemplate is called with a template string that either contains zero '{stem}' markers, contains two or more, or contains stray '{' or '}' characters outside the placeholder. For example, 'prefix{stem}suffix{stem}' or 'output{stem}extra}'.","commonSituations":"Writing an inflection rule template with a typo like '{stme}' instead of '{stem}'. Accidentally including literal braces in the output form. Forgetting the placeholder entirely in a rule that needs stem substitution.","solutions":["Ensure the template contains exactly one '{stem}' placeholder and no other curly braces.","If the output must contain literal braces, escape or restructure the rule so braces only appear around 'stem'.","Remove duplicate '{stem}' occurrences — a rule applies one suffix/prefix transformation per stem."],"exampleFix":"# before\nrules:\n  - id: 'plural-es'\n    output: '{stem}{stem}s'\n# after\nrules:\n  - id: 'plural-es'\n    output: '{stem}s'","handlingStrategy":"validation","validationCode":"# Before building, verify each rule output template has exactly one {stem} and no stray braces.\npython3 -c \"\nimport yaml, sys\nfor f in sys.argv[1:]:\n    d = yaml.safe_load(open(f))\n    owners = (d.get('inflection',{}).get('owners') or [])\n    for owner in owners:\n        for rule in (owner.get('rules') or []):\n            tpl = rule.get('output','')\n            remainder = tpl.replace('{stem}', '')\n            if tpl.count('{stem}') != 1 or '{' in remainder or '}' in remainder:\n                print(f'{f}: rule {rule.get(\"id\",\"\")!r} template invalid: {tpl!r}')\n\" src/Humanizer/Locales/*.yml","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always type the placeholder as {stem} — use copy-paste from a working rule to avoid typos.","Never include literal curly braces in rule output forms; use angle brackets or parentheses for grouping if needed.","Validate templates in a pre-commit hook before building the full solution."],"tags":["inflection","source-generator","rules","template","yaml","build-time"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}