Humanizr/Humanizer · error · InvalidOperationException

Enum members require an enum type.

Error message

Enum members require an enum type.

What it means

A contract member with kind 'enum' must specify an EnumType (the C# enum type name) so the factory can emit a fully qualified enum member access. CreateEnumValue checks for null EnumType and throws because it cannot construct 'null.Value' — the enum type name is load-bearing for code generation.

Source

Thrown at src/Humanizer.SourceGenerators/Generators/ProfileCatalogs/NumberToWordsEngineContractFactory.cs:444

        static string CreateObjectValue(JsonElement root, EngineContractMember member, bool useCultureParameter)
        {
            var objectRoot = string.IsNullOrWhiteSpace(member.SourcePath)
                ? root
                : EngineContractUtilities.GetRequiredElement(root, member.SourcePath);

            // Nested profile objects let the YAML stay grouped by meaning while the generated code
            // still calls an explicit runtime constructor shape. This keeps the authoring surface
            // readable without paying any runtime mapping cost.
            return member.TypeName is null
                ? "new(" + CreateConstructorValues(objectRoot, member.Members, useCultureParameter) + ")"
                : "new " + member.TypeName + "(" + CreateConstructorValues(objectRoot, member.Members, useCultureParameter) + ")";
        }

        static string CreateEnumValue(JsonElement root, EngineContractMember member)
        {
            if (member.EnumType is null)
            {
                throw new InvalidOperationException("Enum members require an enum type.");
            }

            return member.EnumType + "." + ToEnumMemberName(GetStringValue(root, member));
        }

        static string CreateNullableIntStringDictionaryValue(JsonElement root, EngineContractMember member)
        {
            if (EngineContractUtilities.TryGetElement(root, member.SourcePath, out _))
            {
                return CreateNullableIntStringFrozenDictionaryExpression(root, EngineContractUtilities.GetLeafPropertyName(member.SourcePath));
            }

            return member.MissingValue switch
            {
                "empty" => "FrozenDictionary<int, string>.Empty",
                _ => "null"
            };
        }

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Set the enumType parameter on the EngineContractMember to the fully qualified C# enum type name (e.g., 'BillionStrategy').
  2. If the member should not be an enum, change its kind to 'string' or another appropriate kind.
  3. Verify the positional argument order in the EngineContractMember constructor matches the parameter list.

Example fix

// before
new EngineContractMember(
    kind: "enum",
    sourcePath: "billionStrategy",
    enumType: null,
    ...)
// after
new EngineContractMember(
    kind: "enum",
    sourcePath: "billionStrategy",
    enumType: "BillionStrategy",
    ...)
Defensive patterns

Strategy: validation

Validate before calling

// Validate enum members have a type before generation:
foreach (var m in contract.Members)
    if (m.Kind == "enum" && string.IsNullOrEmpty(m.EnumType))
        throw new ArgumentException("Enum member requires EnumType.");

Type guard

static bool IsEnumMemberValid(EngineContractMember m) =>
    m.Kind != "enum" || !string.IsNullOrEmpty(m.EnumType);

Prevention

When it happens

Trigger: An EngineContractMember is constructed with kind='enum' but enumType=null. CreateEnumValue at line 442 throws. This is a generator-level contract definition bug, not a locale YAML problem.

Common situations: Defining a new contract member for an enum-backed property and forgetting to set the EnumType parameter. Refactoring a member from string to enum without adding the enum type name. A positional argument mismatch in the constructor call shifting values.

Related errors


AI-assisted analysis of Humanizr/Humanizer@ffc2b77c0f (2026-08-13). Data as JSON: /api/errors/1d22838254668c71. Report an issue: GitHub.