{"record":{"id":"6436b71901a3cdd8","repo":"Humanizr/Humanizer","slug":"missing-required-string-property-member-sourcepa","errorCode":null,"errorMessage":"Missing required string property '{member.SourcePath}'.","messagePattern":"Missing required string property '(.+?)'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/NumberToWordsEngineContractFactory.cs","lineNumber":557,"sourceCode":"\n        static string GetStringValue(JsonElement root, EngineContractMember member)\n        {\n            if (EngineContractUtilities.TryGetOptionalString(root, member.SourcePath, out var value))\n            {\n                return value!;\n            }\n\n            if (member.FallbackSourcePath is not null && EngineContractUtilities.TryGetOptionalString(root, member.FallbackSourcePath, out var fallback))\n            {\n                return fallback!;\n            }\n\n            if (member.DefaultValue is not null)\n            {\n                return member.DefaultValue;\n            }\n\n            throw new InvalidOperationException($\"Missing required string property '{member.SourcePath}'.\");\n        }\n\n        static string? GetOptionalStringValue(JsonElement root, EngineContractMember member)\n        {\n            if (EngineContractUtilities.TryGetOptionalString(root, member.SourcePath, out var value))\n            {\n                return value;\n            }\n\n            if (member.FallbackSourcePath is not null && EngineContractUtilities.TryGetOptionalString(root, member.FallbackSourcePath, out var fallback))\n            {\n                return fallback;\n            }\n\n            return member.DefaultValue;\n        }\n\n        static bool GetBooleanValue(JsonElement root, EngineContractMember member)","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/NumberToWordsEngineContractFactory.cs#L539-L575","documentation":"For a contract member of kind 'string', the factory tries to resolve the value from the primary source path, then the fallback source path, then the default value. If all three are absent, GetStringValue throws because it has no value to emit in the generated constructor call. This typically means the locale YAML is missing a required property.","triggerScenarios":"An EngineContractMember with kind='string' (or kind='enum' which delegates to GetStringValue) has no value at SourcePath in the locale YAML, no FallbackSourcePath, and no DefaultValue configured. The method exhausts all resolution options and throws.","commonSituations":"A locale YAML omits a required property that the engine contract expects (e.g., 'millionSingularWord' for a billion-strategy engine). Adding a new required string member to a contract without providing a default, breaking locales that don't define it. Renaming a YAML property without updating existing locales.","solutions":["Add the missing YAML property to the locale file at the path specified in the error message (member.SourcePath).","If the property is optional, set a DefaultValue on the EngineContractMember so absent values fall back gracefully.","If the property was renamed, update either the contract's SourcePath or the locale YAML to match."],"exampleFix":"# before (fr.yml — missing millionSingularWord)\nnumberToWords:\n  engine: 'billion-strategy'\n  cardinal:\n    billionStrategy: 'billion-word'\n# after\nnumberToWords:\n  engine: 'billion-strategy'\n  cardinal:\n    millionSingularWord: 'million'\n    millionPluralWord: 'millions'\n    billionStrategy: 'billion-word'\n    billionSingularWord: 'milliard'\n    billionPluralWord: 'milliards'","handlingStrategy":"validation","validationCode":"# Before building, verify required string properties exist in the locale YAML for each engine.\npython3 -c \"\nimport yaml, sys\n# Map engine -> required root-level properties (simplified; extend per engine)\nrequired = {\n    'billion-strategy': ['cardinal.millionSingularWord','cardinal.millionPluralWord','cardinal.billionStrategy'],\n}\nfor f in sys.argv[1:]:\n    d = yaml.safe_load(open(f))\n    n2w = d.get('numberToWords') or {}\n    engine = n2w.get('engine')\n    for path in required.get(engine,[]):\n        obj = n2w\n        for part in path.split('.'):\n            obj = (obj or {}).get(part)\n            if obj is None: break\n        if obj is None:\n            print(f'{f}: engine {engine!r} missing required property {path}')\n\" src/Humanizer/Locales/*.yml","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When adding a new locale, copy from a working locale with the same engine and fill all properties.","Set DefaultValue on optional string members in the contract so absent YAML values do not break generation.","Document required YAML properties per engine in the locale authoring guide."],"tags":["source-generator","number-to-words","contract","string","missing-property","yaml","build-time"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}