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.

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.

Appendix: 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)