Humanizr/Humanizer · error · InvalidOperationException

Builder members require a builder name.

Error message

Builder members require a builder name.

What it means

A contract member with kind 'builder' must specify a Builder name (e.g., 'billion-strategy-cardinal-scale-array') so the factory can dispatch to the correct expression generator. CreateBuilderValue checks for null Builder and throws because the switch at line 498 requires a concrete builder string.

Source

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

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

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

        static string CreateBuilderValue(JsonElement root, EngineContractMember member)
        {
            if (member.Builder is null)
            {
                throw new InvalidOperationException("Builder members require a builder name.");
            }

            if (member.Builder == "billion-strategy-cardinal-scale-array" &&
                !EngineContractUtilities.TryGetElement(root, member.SourcePath, out _))
            {
                return CreateLegacyBillionStrategyCardinalScaleArrayExpression(root);
            }

            var builderRoot = string.IsNullOrWhiteSpace(member.SourcePath)
                ? root
                : EngineContractUtilities.GetRequiredElement(root, member.SourcePath);

            // Builders exist for repeated structured rows such as scale tables, suffix rules, and
            // gender-ending records. Keeping these explicit makes the generated output easier to
            // audit than trying to encode a second generic mini-language for nested collections.
            return member.Builder switch
            {
                "harmony-ordinal-scale-array" => CreateHarmonyOrdinalScaleArrayExpression(builderRoot),

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Set the builder parameter to one of the ~40 supported builder names listed in the switch at line 498-536.
  2. If the member should not use a builder, change its kind to a direct value kind like 'string' or 'int64'.
  3. If a new builder is needed, add a case to the switch and implement the corresponding CreateXxx expression method.

Example fix

// before
new EngineContractMember(
    kind: "builder",
    sourcePath: "scales",
    builder: null,
    ...)
// after
new EngineContractMember(
    kind: "builder",
    sourcePath: "scales",
    builder: "billion-strategy-cardinal-scale-array",
    ...)
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

static bool IsBuilderMemberValid(EngineContractMember m) =>
    m.Kind != "builder" || !string.IsNullOrEmpty(m.Builder);

Prevention

When it happens

Trigger: An EngineContractMember is constructed with kind='builder' but builder=null. CreateBuilderValue at line 480 throws. This is a generator-level contract configuration error.

Common situations: Adding a builder-type member to a contract and forgetting to set the Builder parameter. Changing a member kind to 'builder' without specifying which builder. Positional argument shift in the constructor dropping the builder value.

Related errors


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