{"record":{"id":"cf2a10eb7df8c341","repo":"Humanizr/Humanizer","slug":"locale-localecode-surfaces-list-must-define-e","errorCode":null,"errorMessage":"Locale '{localeCode}.surfaces.list' must define 'engine'.","messagePattern":"Locale '(.+?)\\.surfaces\\.list' must define 'engine'\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Humanizer.SourceGenerators/Common/CanonicalLocaleAuthoring.cs","lineNumber":579,"sourceCode":"                    {\n                        RejectExplicitDefaultEngines(localeCode, $\"{path}[{index}]\", sequence.Items[index]);\n                    }\n\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        static SimpleYamlMapping NormalizeListSurface(string localeCode, SimpleYamlMapping mapping)\n        {\n            if (mapping.TryGetValue(\"value\", out _))\n            {\n                return mapping;\n            }\n\n            var engine = mapping.GetScalar(\"engine\")\n                ?? throw new InvalidOperationException($\"Locale '{localeCode}.surfaces.list' must define 'engine'.\");\n            var pairTemplate = mapping.GetScalar(\"pairTemplate\");\n            var finalTemplate = mapping.GetScalar(\"finalTemplate\");\n            var serialTemplate = mapping.GetScalar(\"serialTemplate\");\n            if (engine == \"oxford\" && pairTemplate is null && finalTemplate is null)\n            {\n                return mapping;\n            }\n\n            if (pairTemplate is null)\n            {\n                throw new InvalidOperationException(\n                    $\"Locale '{localeCode}.surfaces.list' must define canonical list templates or legacy 'value'.\");\n            }\n\n            finalTemplate ??= pairTemplate;\n\n            var values = ImmutableDictionary.CreateBuilder<string, SimpleYamlValue>(StringComparer.Ordinal);\n            var oxfordComma = ReadBoolean(mapping, \"oxfordComma\");","sourceCodeStart":561,"sourceCodeEnd":597,"githubUrl":"https://github.com/Humanizr/Humanizer/blob/ffc2b77c0f30d2fb176875841424379319d0ae9b/src/Humanizer.SourceGenerators/Common/CanonicalLocaleAuthoring.cs#L561-L597","documentation":"Thrown by NormalizeListSurface when a locale's surfaces.list block has neither a legacy 'value' scalar nor an 'engine' scalar. The list/collection-formatter surface must declare which engine it uses (e.g. oxford, conjunction, delimited, clitic) unless it provides a legacy 'value' separator string. Without one or the other the generator cannot determine how to join list items.","triggerScenarios":"ToLocaleDefinition calls NormalizeListSurface for every locale with a 'list' surface. If the mapping has no 'value' key and no 'engine' key, GetScalar returns null and the ?? operator throws. For example: 'surfaces:\\n  list:\\n    pairTemplate: \"{0}, {1}\"' without an engine line.","commonSituations":"A contributor writes a list surface from scratch and forgets the engine line, or renames 'engine' to something else. Also happens when migrating from a legacy collectionFormatter that was a bare scalar (which maps to engine only) but the new file uses mapping syntax without re-adding engine.","solutions":["Add an 'engine' scalar to the surfaces.list block, e.g. 'engine: oxford' or 'engine: conjunction'.","Alternatively, provide a legacy 'value' scalar (a bare separator string like ', ') if you only need simple delimiter joining.","Check the locale file against a known-good locale (e.g. en.yml) to confirm the expected list surface shape."],"exampleFix":"# before\nsurfaces:\n  list:\n    pairTemplate: \"{0}, {1}\"\n# after\nsurfaces:\n  list:\n    engine: oxford\n    pairTemplate: \"{0}, {1}\"","handlingStrategy":"validation","validationCode":"// Check surfaces.list has engine or value before invoking the generator\nstatic bool ListSurfaceIsValid(SimpleYamlMapping surfaces)\n{\n    if (!surfaces.TryGetValue(\"list\", out var list) || list is not SimpleYamlMapping listMap)\n        return true; // no list surface, no problem\n    return listMap.TryGetValue(\"value\", out _) || listMap.GetScalar(\"engine\") is not null;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always include 'engine' in surfaces.list blocks unless using the legacy 'value' scalar.","Cross-reference the list surface against en.yml or another shipped locale.","Run dotnet build after every locale edit to catch source-generator errors early."],"tags":["source-generator","locale","yaml","list-formatting","collection-formatter"],"backgroundTag":null,"analyzedSha":"ffc2b77c0f30d2fb176875841424379319d0ae9b","analyzedAt":"2026-08-13T21:42:34.584Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}