reactiveui/refit · error · ArgumentException

HeaderCollection parameter of type {parameterArray[i].Parame

Error message

HeaderCollection parameter of type {parameterArray[i].ParameterType.Name} is not assignable from IDictionary<string, string>

What it means

A parameter marked as a header collection must accept IDictionary<string,string>, because Refit assigns header collections via that interface (the comment notes IDictionary is chosen to enforce unique keys). If the parameter type is not assignable from IDictionary<string,string> (e.g. a List, an array, a custom type without that interface), this throws.

Source

Thrown at src/Refit.Reflection/RestMethodInfoInternal.AttributeReading.cs:129

    /// <exception cref="ArgumentException">A header collection parameter is not assignable from
    /// <c>IDictionary&lt;string, string&gt;</c>, or more than one parameter carries
    /// <see cref="HeaderCollectionAttribute"/>.</exception>
    internal static int GetHeaderCollectionParameterIndex(ParameterInfo[] parameterArray, ParameterAttributeSet[] sets)
    {
        var headerIndex = -1;

        for (var i = 0; i < parameterArray.Length; i++)
        {
            if (sets[i].HeaderCollection is null)
            {
                continue;
            }

            // Opted for IDictionary<string, string> semantics here as opposed to the looser
            // IEnumerable<KeyValuePair<string, string>> because IDictionary enforces unique keys.
            if (!parameterArray[i].ParameterType.IsAssignableFrom(typeof(IDictionary<string, string>)))
            {
                throw new ArgumentException(
                    $"HeaderCollection parameter of type {parameterArray[i].ParameterType.Name} is not assignable from IDictionary<string, string>");
            }

            // Throw if there is already a HeaderCollection parameter.
            if (headerIndex >= 0)
            {
                throw new ArgumentException("Only one parameter can be a HeaderCollection parameter");
            }

            headerIndex = i;
        }

        return headerIndex;
    }

    /// <summary>Builds the map of parameter indexes to request property keys.</summary>
    /// <param name="parameterArray">The array of method parameters.</param>
    /// <param name="sets">The classified attribute set for each parameter.</param>

View on GitHub (pinned to b455f65ecc)

Solutions

  1. Declare the header collection parameter as IDictionary<string,string> (or a type assignable from it, e.g. Dictionary<string,string>).
  2. For multi-valued headers use the supported header binding rather than a custom collection type.
  3. Confirm the parameter carries the correct header-collection attribute.

Example fix

// before
[Get("/search")] Task SearchAsync([HeaderCollection] List<KeyValuePair<string,string>> h);

// after
[Get("/search")] Task SearchAsync([HeaderCollection] IDictionary<string,string> h);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!typeof(T).IsAssignableFrom(typeof(IDictionary<string,string>)))
    throw new ArgumentException("Header collection parameter must accept IDictionary<string,string>.");

Type guard

static bool IsHeaderCollectionType<T>() => typeof(T).IsAssignableFrom(typeof(IDictionary<string,string>));

Prevention

When it happens

Trigger: Declaring a header-collection parameter (a parameter bound to multiple headers) whose declared type does not implement/accept IDictionary<string,string>, such as IEnumerable, a List<>, a record, or a Dictionary with non-string keys.

Common situations: Binding headers to a Dictionary<string,string[]> or a custom multi-value type; using a collection type Refit cannot populate; misreading the header-collection requirement.

Related errors


AI-assisted analysis of reactiveui/refit@b455f65ecc (2026-08-13). Data as JSON: /api/errors/964faf32c476156e. Report an issue: GitHub.