RicoSuter/NSwag · error · InvalidOperationException
The operation ' ' has more than one body parameter.
Error message
The operation '{operationDescription.Operation.OperationId}' has more than one body parameter. What it means
The WebApi (non-ASP.NET-Core) operation generator throws when an operation's actual parameters contain more than one parameter with OpenApiParameterKind.Body after processing. An OpenAPI operation permits only a single body parameter, so EnsureSingleBodyParameter (called from Process) enforces this and throws InvalidOperationException with the operation's OperationId.
Solutions
- Merge the multiple body inputs into one request DTO and use it as the single body parameter.
- Decorate the non-body parameter with [FromUri] (Web API 2) so it is treated as route/query instead of body.
- Mark exactly one parameter [FromBody] and give the others explicit non-body binding sources.
- If a custom NSwag processor adds the extra body parameter, correct it to emit at most one.
Example fix
// before public void Post(OrderDto order, AuditContext audit) // both become body // after public void Post([FromBody] OrderDto order, [FromUri] AuditContext audit)
Defensive patterns
Strategy: validation
Validate before calling
// Web API 2: exactly one implicit/explicit body parameter per action
var bodyParams = actionDescriptor.GetParameters()
.Where(p => !p.GetCustomAttributes(typeof(FromUriAttribute), false).Any());
bool tooManyBodies = bodyParams.Count() > 1; Try / catch
try { document = generator.Generate(settings); }
catch (InvalidOperationException ex) when (ex.Message.Contains("more than one body parameter"))
{
logger.LogError(ex, "Web API operation has multiple body parameters");
} Prevention
- Mark every non-body parameter [FromUri] explicitly in Web API 2 actions.
- Merge multiple payloads into a single request DTO.
- Generate the Swagger document in tests to catch binding-source mistakes.
When it happens
Trigger: Generating a document for a Web API 2 / OWIN controller action with multiple parameters that resolve to body kind (e.g. two complex typed parameters without [FromBody]/[FromUri] markers, so both default to body), or two parameters explicitly marked [FromBody].
Common situations: Classic ASP.NET Web API actions taking two complex types where one was intended as [FromUri]; migrating actions between frameworks and keeping duplicate body bindings; custom parameter processors adding a second body parameter.
Related errors
- The operation ' ' has more than one body parameter.
- The method ' ' on path ' ' is registered multiple times for…
- The method ' ' on path ' ' is registered multiple times for…
- Multiple body parameters found in operation
- NSwag requires the assembly
AI-assisted analysis of RicoSuter/NSwag@63daf8fcc3 (2026-09-14).
Data as JSON: /api/errors/b8d314fefb31979a.
Report an issue: GitHub.
Appendix: source
Thrown at src/NSwag.Generation.WebApi/Processors/OperationParameterProcessor.cs:272
/// </summary>
/// <param name="operationDescription">Operation to check.</param>
/// <param name="schemaType">Schema type.</param>
private static void UpdateNullableRawOperationParameters(OpenApiOperationDescription operationDescription, SchemaType schemaType)
{
if (schemaType == SchemaType.OpenApi3)
{
foreach (OpenApiParameter openApiParameter in operationDescription.Operation.Parameters)
{
openApiParameter.IsNullableRaw = null;
}
}
}
private static void EnsureSingleBodyParameter(OpenApiOperationDescription operationDescription)
{
if (operationDescription.Operation.ActualParameters.Count(p => p.Kind == OpenApiParameterKind.Body) > 1)
{
throw new InvalidOperationException($"The operation '{operationDescription.Operation.OperationId}' has more than one body parameter.");
}
}
private static void UpdateConsumedTypes(OpenApiOperationDescription operationDescription)
{
if (operationDescription.Operation.ActualParameters.Any(p => p.IsBinary || p.ActualSchema.IsBinary))
{
operationDescription.Operation.TryAddConsumes("multipart/form-data");
}
}
private static void RemoveUnusedPathParameters(OpenApiOperationDescription operationDescription, string httpPath)
{
operationDescription.Path = Regex.Replace(httpPath, "{(.*?)(:(([^/]*)?))?}", match =>
{
var parameterName = match.Groups[1].Value.TrimEnd('?');
if (operationDescription.Operation.ActualParameters.Any(p => p.Kind == OpenApiParameterKind.Path && string.Equals(p.Name, parameterName, StringComparison.OrdinalIgnoreCase)))
{View on GitHub (pinned to 63daf8fcc3)