Humanizr/Humanizer · error · InvalidOperationException

Expected JSON array.

Error message

Expected JSON array.

What it means

CreateStringArrayExpression (GenerationHelpers.cs:265-275) accepts two shapes for a string-array field: a JSON Array (plain list) or a JSON Object (sparse numeric-slot mapping). Any other top-level kind (scalar, or sometimes null) throws. This emits unitsMap/tensMap-style C# string[] arrays from locale data.

Source

Thrown at src/Humanizer.SourceGenerators/Common/GenerationHelpers.cs:274

    ///    <code>
    ///    tensMap:
    ///      2: 'twenty'
    ///      3: 'thirty'
    ///    </code>
    ///
    /// The numeric-slot form keeps YAML readable by replacing alignment hacks such as leading
    /// empty strings with explicit indices. Missing slots are emitted as empty strings.
    /// </summary>
    static string CreateStringArrayExpression(JsonElement arrayElement)
    {
        if (arrayElement.ValueKind == JsonValueKind.Object)
        {
            return CreateNumericSlotStringArrayExpression(arrayElement);
        }

        if (arrayElement.ValueKind != JsonValueKind.Array)
        {
            throw new InvalidOperationException("Expected JSON array.");
        }

        var builder = new StringBuilder("new string[] { ");
        var first = true;

        foreach (var item in arrayElement.EnumerateArray())
        {
            if (item.ValueKind != JsonValueKind.String)
            {
                continue;
            }

            if (!first)
            {
                builder.Append(", ");
            }

            builder.Append(QuoteLiteral(item.GetString()!));

View on GitHub (pinned to ffc2b77c0f)

Solutions

  1. Provide the value as a YAML/JSON array (['zero','one',...]) or as an indexed mapping ({2: 'twenty', 3: 'thirty'}).
  2. Check YAML indentation so the list items nest under the key.
  3. Verify the contract sourcePath points at the list node, not a parent scalar.

Example fix

# before
unitsMap: 'zero'
# after
unitsMap:
  - 'zero'
  - 'one'
  - 'two'
Defensive patterns

Strategy: type-guard

Validate before calling

// A string-array field must be Array or Object (C#)
if (arrayElement.ValueKind != JsonValueKind.Array && arrayElement.ValueKind != JsonValueKind.Object)
    throw new InvalidOperationException("Expected JSON array or indexed object for string array");

Type guard

static bool IsStringArrayShape(JsonElement el) =>
    el.ValueKind == JsonValueKind.Array || el.ValueKind == JsonValueKind.Object;

Prevention

When it happens

Trigger: A field expected to be a list or indexed mapping is instead a scalar (e.g. 'unitsMap: "zero"') or null.

Common situations: A YAML list collapsed to a scalar through indentation; a value left as a placeholder string; wrong node referenced by a contract sourcePath.

Related errors


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