dotnet/yarp · error · ArgumentException

Header values must have at least one value.

Error message

Header values must have at least one value.

What it means

The `HeaderMatcher` constructor requires at least one value for matching modes that compare values (`ExactHeader`, `HeaderPrefix`, `Contains`, `NotContains`). Only `Exists` and `NotExists` modes are exempt because they don't need values. This validation ensures the matcher has the data it needs to perform the comparison.

Source

Thrown at src/ReverseProxy/Routing/HeaderMatcher.cs:29

/// <summary>
/// A request header matcher used during routing.
/// </summary>
internal sealed class HeaderMatcher
{
    /// <summary>
    /// Creates a new instance.
    /// </summary>
    public HeaderMatcher(string name, IReadOnlyList<string>? values, HeaderMatchMode mode, bool isCaseSensitive)
    {
        if (string.IsNullOrEmpty(name))
        {
            throw new ArgumentException("A header name is required.", nameof(name));
        }
        if ((mode != HeaderMatchMode.Exists && mode != HeaderMatchMode.NotExists)
            && (values is null || values.Count == 0))
        {
            throw new ArgumentException("Header values must have at least one value.", nameof(values));
        }
        if ((mode == HeaderMatchMode.Exists || mode == HeaderMatchMode.NotExists) && values?.Count > 0)
        {
            throw new ArgumentException($"Header values must not be specified when using '{mode}'.", nameof(values));
        }
        if (values is not null && values.Any(string.IsNullOrEmpty))
        {
            throw new ArgumentNullException(nameof(values), "Header values must be not be empty.");
        }

        Name = name;
        Values = values?.ToArray() ?? Array.Empty<string>();
        Mode = mode;
        Comparison = isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase;
        Separator = name.Equals(HeaderNames.Cookie, StringComparison.OrdinalIgnoreCase) ? ';' : ',';
    }

    /// <summary>

View on GitHub (pinned to bd11867bee)

Solutions

  1. If you want to match on header existence, set `Mode` to `"Exists"` (or `"NotExists"`) and omit values — this is the most common fix.
  2. If matching on specific values, populate the `Values` array with at least one value: `{ "Header": "X-Key", "Values": ["expected-value"] }`.
  3. Verify the JSON structure — `Values` must be an array of strings, not a single string value.
  4. Check for trailing commas or missing entries in the values array that could produce an empty collection.

Example fix

// before — values missing with default ExactHeader mode
"Headers": [
  { "Header": "X-Trace-Id" } // throws — no values, default mode is ExactHeader
]
// after — use Exists mode if you only need header presence
"Headers": [
  { "Header": "X-Trace-Id", "Mode": "Exists" }
]
// or provide values if you need value matching
"Headers": [
  { "Header": "X-Env", "Values": ["prod", "staging"] }
]
Defensive patterns

Strategy: validation

Validate before calling

// Validate header match values/mode before adding to route
static bool IsValidHeaderMatchValues(HeaderMatchConfig match)
{
    var needsValues = match.Mode is not ("Exists" or "NotExists");
    return !needsValues || (match.Values is { Count: > 0 });
}

Type guard

static bool IsValidHeaderMatcherConfig(string name, IReadOnlyList<string>? values, HeaderMatchMode mode)
    => !string.IsNullOrEmpty(name)
    && ((mode is HeaderMatchMode.Exists or HeaderMatchMode.NotExists) || (values is { Count: > 0 }));

Try / catch

// Not applicable — fix the configuration to provide values or use Exists mode.

Prevention

When it happens

Trigger: A route header match is configured with a mode like `ExactHeader` (the default) but with an empty or null `Values` array (e.g., `{ "Header": "X-Key", "Values": [] }` or `{ "Header": "X-Key" }` without specifying values and without setting `Mode` to `Exists`). The constructor check at line 26-30 detects `values is null || values.Count == 0` and throws.

Common situations: A developer wants to check header *existence* but doesn't set `Mode: "Exists"` — the default mode is `ExactHeader`, which requires values. A config template has a placeholder values array that was never populated. A dynamic config provider omits values when they should have been populated.

Related errors


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/2ed2be8b6aa0136f. Report an issue: GitHub.