{"record":{"id":"d0572a9ac96582d0","repo":"reactiveui/refit","slug":"multiple-complex-types-found-specify-one-paramete","errorCode":null,"errorMessage":"Multiple complex types found. Specify one parameter as the body using BodyAttribute","messagePattern":"Multiple complex types found\\. Specify one parameter as the body using BodyAttribute","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"src/Refit.Reflection/RestMethodInfoInternal.cs","lineNumber":676,"sourceCode":"        var refParamIndex = -1;\n        for (var i = 0; i < parameterArray.Length; i++)\n        {\n            var parameter = parameterArray[i];\n            if (parameter.ParameterType.GetTypeInfo().IsValueType\n                || parameter.ParameterType == typeof(string)\n                || sets[i].Query is not null\n                || sets[i].HeaderCollection is not null\n                || sets[i].Property is not null\n\n                // A [Url] Uri parameter supplies the request URI, never the implicit body.\n                || sets[i].Url is not null)\n            {\n                continue;\n            }\n\n            if (refParam is not null)\n            {\n                throw new ArgumentException(\n                    \"Multiple complex types found. Specify one parameter as the body using BodyAttribute\");\n            }\n\n            refParam = parameter;\n            refParamIndex = i;\n        }\n\n        return refParam is null\n            ? null\n            : Tuple.Create(\n                BodySerializationMethod.Serialized,\n                RefitSettings.Buffered,\n                refParamIndex);\n    }\n\n    /// <summary>Holds the parsed forms of a route parameter name extracted from a URL template.</summary>\n    /// <param name=\"RawName\">The raw parameter name from the URL template.</param>\n    /// <param name=\"Name\">The normalized parameter name with any round-tripping prefix removed.</param>","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/reactiveui/refit/blob/b455f65ecc4c97d092317e349cb775f9cfc6bcdf/src/Refit.Reflection/RestMethodInfoInternal.cs#L658-L694","documentation":"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].","triggerScenarios":"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.","commonSituations":"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.","solutions":["Mark exactly one complex parameter with [Body] to make the body explicit.","Annotate the non-body complex parameter with [Query] (or [AliasAs]/[HeaderCollection]) so it is excluded from body detection.","If both truly belong in the body, merge them into a single composite DTO."],"exampleFix":"// before (POST with two unannotated reference types)\nTask CreateAsync(Order order, Filter filter);\n\n// after — explicit body, filter goes to query\nTask CreateAsync([Body] Order order, [Query] Filter filter);","handlingStrategy":"validation","validationCode":"// For POST/PUT/PATCH, flag ambiguous unannotated reference-type parameters.\nusing System.Reflection;\nvar bodyVerbs = new[] { \"POST\", \"PUT\", \"PATCH\" };\nforeach (var m in typeof(IMyApi).GetMethods())\n{\n    var attr = m.GetCustomAttribute<HttpMethodAttribute>();\n    if (attr is null || !bodyVerbs.Contains(attr.Method)) continue;\n    var complex = m.GetParameters()\n        .Where(p => !p.ParameterType.IsValueType && p.ParameterType != typeof(string)\n                    && p.GetCustomAttribute<BodyAttribute>() is null\n                    && p.GetCustomAttribute<QueryAttribute>() is null)\n        .ToList();\n    if (complex.Count > 1)\n        throw new InvalidOperationException($\"{m.Name}: multiple complex params — mark one [Body] or the others [Query].\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always annotate the body parameter explicitly with [Body] on POST/PUT/PATCH when there's more than one complex parameter.","Bind filter/options objects with [Query] so they're excluded from body detection.","Review interface signatures during code review for unannotated complex params."],"tags":["body","ambiguous-binding","reflection-builder"],"backgroundTag":null,"analyzedSha":"b455f65ecc4c97d092317e349cb775f9cfc6bcdf","analyzedAt":"2026-08-13T21:20:57.878Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}