reactiveui/refit · error · ArgumentException

Multiple complex types found. Specify one parameter as the…

Error message

Multiple complex types found. Specify one parameter as the body using BodyAttribute

What it means

Thrown during implicit body detection for POST/PUT/PATCH when, after excluding params explicitly bound by other attributes (Body, Query, Header, Property, Url, etc.), more than one reference-type parameter remains. Refit would implicitly serialize the first such parameter as the body, but with two ambiguous complex parameters it cannot choose, so it demands you disambiguate with [Body].

Solutions

  1. Mark exactly one complex parameter with [Body] to make the body explicit.
  2. Annotate the non-body complex parameter with [Query] (or [AliasAs]/[HeaderCollection]) so it is excluded from body detection.
  3. If both truly belong in the body, merge them into a single composite DTO.

Example fix

// before (POST with two unannotated reference types)
Task CreateAsync(Order order, Filter filter);

// after — explicit body, filter goes to query
Task CreateAsync([Body] Order order, [Query] Filter filter);
Defensive patterns

Strategy: validation

Validate before calling

// For POST/PUT/PATCH, flag ambiguous unannotated reference-type parameters.
using System.Reflection;
var bodyVerbs = new[] { "POST", "PUT", "PATCH" };
foreach (var m in typeof(IMyApi).GetMethods())
{
    var attr = m.GetCustomAttribute<HttpMethodAttribute>();
    if (attr is null || !bodyVerbs.Contains(attr.Method)) continue;
    var complex = m.GetParameters()
        .Where(p => !p.ParameterType.IsValueType && p.ParameterType != typeof(string)
                    && p.GetCustomAttribute<BodyAttribute>() is null
                    && p.GetCustomAttribute<QueryAttribute>() is null)
        .ToList();
    if (complex.Count > 1)
        throw new InvalidOperationException($"{m.Name}: multiple complex params — mark one [Body] or the others [Query].");
}

Prevention

When it happens

Trigger: A POST/PUT/PATCH method with two unannotated reference-type parameters that aren't strings and aren't bound to query/header/url, e.g. `Task PostAsync(Order order, Customer customer)`. The loop finds a second refParam and throws.

Common situations: Sending a DTO plus another object (e.g. a file or nested model) without specifying which is the body; forgetting [Query] on a filter object that should go in the query string alongside a body DTO.

Related errors


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

Appendix: source

Thrown at src/Refit.Reflection/RestMethodInfoInternal.cs:676

        var refParamIndex = -1;
        for (var i = 0; i < parameterArray.Length; i++)
        {
            var parameter = parameterArray[i];
            if (parameter.ParameterType.GetTypeInfo().IsValueType
                || parameter.ParameterType == typeof(string)
                || sets[i].Query is not null
                || sets[i].HeaderCollection is not null
                || sets[i].Property is not null

                // A [Url] Uri parameter supplies the request URI, never the implicit body.
                || sets[i].Url is not null)
            {
                continue;
            }

            if (refParam is not null)
            {
                throw new ArgumentException(
                    "Multiple complex types found. Specify one parameter as the body using BodyAttribute");
            }

            refParam = parameter;
            refParamIndex = i;
        }

        return refParam is null
            ? null
            : Tuple.Create(
                BodySerializationMethod.Serialized,
                RefitSettings.Buffered,
                refParamIndex);
    }

    /// <summary>Holds the parsed forms of a route parameter name extracted from a URL template.</summary>
    /// <param name="RawName">The raw parameter name from the URL template.</param>
    /// <param name="Name">The normalized parameter name with any round-tripping prefix removed.</param>

View on GitHub (pinned to b455f65ecc)