Humanizr/Humanizer · error · InvalidOperationException
Locale '{localeCode}.{featureName}' must declare an 'engine'
Error message
Locale '{localeCode}.{featureName}' must declare an 'engine'. What it means
The 'collectionFormatter' feature, when provided as a mapping, must declare an 'engine' key so the generator knows which collection-formatting strategy to wire up. Without it the feature is ambiguous and the generator cannot select an implementation.
Source
Thrown at src/Humanizer.SourceGenerators/Common/LocaleYamlCatalog.cs:966
var localEngine = localMapping.GetScalar("engine");
return inheritedEngine is not null &&
localEngine is not null &&
!string.Equals(inheritedEngine, localEngine, StringComparison.Ordinal)
? localMapping
: MergeMappings(path, inheritedMapping, localMapping);
}
// Ordered lexical data such as scale lists and token tables stay explicit.
// A child sequence replaces the parent sequence rather than trying to merge by index.
return localValue;
}
static LocaleFeature CreateMappedFeature(string localeCode, string featureName, SimpleYamlMapping mapping)
{
if (featureName == "collectionFormatter")
{
var engine = mapping.GetScalar("engine")
?? throw new InvalidOperationException($"Locale '{localeCode}.{featureName}' must declare an 'engine'.");
return new LocaleFeature(localeCode, featureName, engine, mapping.GetScalar("value"), null, default, false);
}
if (featureName == "wordsToNumber" &&
string.Equals(mapping.GetScalar("engine"), "token-map", StringComparison.Ordinal))
{
return new LocaleFeature(localeCode, featureName, "lexicon", localeCode, null, ToJsonElement(NormalizeFeatureReferences(localeCode, featureName, mapping)), false);
}
if (featureName is "dateToOrdinalWords" or "dateOnlyToOrdinalWords" &&
string.Equals(mapping.GetScalar("engine"), "default", StringComparison.Ordinal) &&
mapping.GetScalar("pattern") is not null)
{
var values = mapping.Values.SetItem("engine", new SimpleYamlScalar("pattern", isQuoted: true));
mapping = new SimpleYamlMapping(values);
}
if (featureName is "formatter" or "ordinalizer" or "dateToOrdinalWords" or "dateOnlyToOrdinalWords" or "timeOnlyToClockNotation")View on GitHub (pinned to ffc2b77c0f)
Solutions
- Add an 'engine:' key to the collectionFormatter mapping (e.g. 'engine: default' or 'engine: oxford').
- Verify the engine value matches a supported collection formatter engine name.
- If a scalar shorthand is sufficient, replace the mapping with 'collectionFormatter: default'.
Example fix
# before collectionFormatter: value: oxford # after collectionFormatter: engine: oxford value: ""
Defensive patterns
Strategy: validation
Validate before calling
import yaml, pathlib
locales_dir = pathlib.Path('src/Humanizer/Locales')
for f in locales_dir.rglob('*.yml'):
data = yaml.safe_load(f.read_text())
if isinstance(data, dict) and isinstance(data.get('collectionFormatter'), dict):
if 'engine' not in data['collectionFormatter']:
print(f'{f.name}: collectionFormatter mapping must declare "engine"') Prevention
- Always include an 'engine:' key inside a collectionFormatter mapping.
- If only a default engine is needed, use the scalar shorthand 'collectionFormatter: default'.
- Check the engine value against supported collection formatter engine names.
When it happens
Trigger: A locale's 'collectionFormatter:' mapping contains a 'value:' key (or other keys) but omits the required 'engine:' key.
Common situations: Authoring collection formatting for the first time and forgetting the engine selector; partially copying a mapping and dropping the engine line; indentation causing 'engine:' to attach elsewhere.
Related errors
- Locale '{localeCode}.surfaces.list' must define 'engine'.
- Locale '{localeCode}.surfaces.list' must define canonical li
- Locale '{localeCode}.headings' must define '{key}'.
- Locale '{localeCode}.{path}' defines duplicate numeric minut
- Locale '{localeCode}.{path}.{key}' must be a string.
AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13).
Data as JSON: /api/errors/a1e473bbc6868352.
Report an issue: GitHub.