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 ASP.NET Core operation generator throws when, after parameter processing, an operation ends up with more than one parameter of OpenApiParameterKind.Body. OpenAPI allows at most one requestBody/body parameter per operation, so NSwag validates this invariant in EnsureSingleBodyParameter (called from Process) and throws InvalidOperationException.
Solutions
- Merge the multiple body parameters into a single request DTO class and bind that one [FromBody] model.
- Change one parameter's binding source, e.g. move auxiliary data to [FromQuery], [FromRoute], or [FromHeader].
- If extra body params come from a custom IOperationProcessor or parameter filter you added, fix it so only one body parameter is emitted.
- For raw body + metadata, accept the raw body and pass metadata via route/query instead.
Example fix
// before
public IActionResult Save([FromBody] OrderDto order, [FromBody] SaveOptions options)
// after
public class SaveRequest { public OrderDto Order { get; set; } public SaveOptions Options { get; set; } }
public IActionResult Save([FromBody] SaveRequest request) Defensive patterns
Strategy: validation
Validate before calling
// Guard action signatures: at most one [FromBody] parameter per action
bool hasMultipleBodyParams = methodInfo.GetParameters()
.Count(p => p.GetCustomAttributes(typeof(FromBodyAttribute), false).Any()) > 1; Try / catch
try { document = generator.Generate(settings); }
catch (InvalidOperationException ex) when (ex.Message.Contains("more than one body parameter"))
{
logger.LogError(ex, "Operation has multiple body parameters");
} Prevention
- Adopt a convention of one request DTO per action; never accept two [FromBody] parameters.
- Keep auxiliary data in query, route, or header parameters.
- Add a unit test that generates the document for all controllers.
When it happens
Trigger: Generating a document for an action whose signature contains two [FromBody] parameters (e.g. [FromBody] OrderDto order, [FromBody] OptionsDto options), or a custom parameter processor/binding that produces additional body-kind parameters for the same operation.
Common situations: Migrating an action that accepted a raw body plus a JSON payload; combining [FromBody] with a custom model binder registered as body; copy-pasting an action and forgetting to change a parameter's source attribute; third-party attributes injecting extra body parameters.
Related errors
- The method ' ' on path ' ' is registered multiple times for…
- The operation ' ' has more than one body parameter.
- NSwag requires the assembly
- This UI does not support multiple documents per UI: Do not…
- The SwaggerUiRoute cannot contain
AI-assisted analysis of RicoSuter/NSwag@63daf8fcc3 (2026-09-14).
Data as JSON: /api/errors/3e6f1ce923527efc.
Report an issue: GitHub.
Appendix: source
Thrown at src/NSwag.Generation.AspNetCore/Processors/OperationParameterProcessor.cs:255
foreach (var mimeType in mimeTypes)
{
operationDescription.Operation.RequestBody.Content[mimeType] = new OpenApiMediaType
{
Schema = mimeType == "application/json" ? JsonSchema.CreateAnySchema() : new JsonSchema
{
Type = _settings.SchemaSettings.SchemaType == SchemaType.Swagger2 ? JsonObjectType.File : JsonObjectType.String,
Format = _settings.SchemaSettings.SchemaType == SchemaType.Swagger2 ? null : JsonFormatStrings.Binary,
}
};
}
}
}
private static void EnsureSingleBodyParameter(OpenApiOperationDescription operationDescription, List<OpenApiParameter> actualParameters)
{
if (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, List<OpenApiParameter> actualParameters)
{
if (actualParameters.Any(static p => p.IsBinary || p.ActualSchema.IsBinary))
{
operationDescription.Operation.TryAddConsumes("multipart/form-data");
}
}
private static void RemoveUnusedPathParameters(OpenApiOperationDescription operationDescription, List<OpenApiParameter> actualParameters, string httpPath)
{
operationDescription.Path = "/" + unusedPathParametersRegex.Replace(httpPath, match =>
{
var parameterName = match.Groups[1].Value.TrimEnd('?');
if (actualParameters.Any(p => p.Kind == OpenApiParameterKind.Path && string.Equals(p.Name, parameterName, StringComparison.OrdinalIgnoreCase)))
{View on GitHub (pinned to 63daf8fcc3)