{"record":{"id":"3e6f1ce923527efc","repo":"RicoSuter/NSwag","slug":"the-operation-operationdescription-operation-operationid-has","errorCode":null,"errorMessage":"The operation '{operationDescription.Operation.OperationId}' has more than one body parameter.","messagePattern":"The operation '(.+?)' has more than one body parameter\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/NSwag.Generation.AspNetCore/Processors/OperationParameterProcessor.cs","lineNumber":255,"sourceCode":"                foreach (var mimeType in mimeTypes)\n                {\n                    operationDescription.Operation.RequestBody.Content[mimeType] = new OpenApiMediaType\n                    {\n                        Schema = mimeType == \"application/json\" ? JsonSchema.CreateAnySchema() : new JsonSchema\n                        {\n                            Type = _settings.SchemaSettings.SchemaType == SchemaType.Swagger2 ? JsonObjectType.File : JsonObjectType.String,\n                            Format = _settings.SchemaSettings.SchemaType == SchemaType.Swagger2 ? null : JsonFormatStrings.Binary,\n                        }\n                    };\n                }\n            }\n        }\n\n        private static void EnsureSingleBodyParameter(OpenApiOperationDescription operationDescription, List<OpenApiParameter> actualParameters)\n        {\n            if (actualParameters.Count(p => p.Kind == OpenApiParameterKind.Body) > 1)\n            {\n                throw new InvalidOperationException($\"The operation '{operationDescription.Operation.OperationId}' has more than one body parameter.\");\n            }\n        }\n\n        private static void UpdateConsumedTypes(OpenApiOperationDescription operationDescription, List<OpenApiParameter> actualParameters)\n        {\n            if (actualParameters.Any(static p => p.IsBinary || p.ActualSchema.IsBinary))\n            {\n                operationDescription.Operation.TryAddConsumes(\"multipart/form-data\");\n            }\n        }\n\n        private static void RemoveUnusedPathParameters(OpenApiOperationDescription operationDescription, List<OpenApiParameter> actualParameters, string httpPath)\n        {\n            operationDescription.Path = \"/\" + unusedPathParametersRegex.Replace(httpPath, match =>\n            {\n                var parameterName = match.Groups[1].Value.TrimEnd('?');\n                if (actualParameters.Any(p => p.Kind == OpenApiParameterKind.Path && string.Equals(p.Name, parameterName, StringComparison.OrdinalIgnoreCase)))\n                {","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/RicoSuter/NSwag/blob/63daf8fcc3a25151b62eb4b326a1e8ea048a0d41/src/NSwag.Generation.AspNetCore/Processors/OperationParameterProcessor.cs#L237-L273","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npublic IActionResult Save([FromBody] OrderDto order, [FromBody] SaveOptions options)\n\n// after\npublic class SaveRequest { public OrderDto Order { get; set; } public SaveOptions Options { get; set; } }\npublic IActionResult Save([FromBody] SaveRequest request)","handlingStrategy":"validation","validationCode":"// Guard action signatures: at most one [FromBody] parameter per action\nbool hasMultipleBodyParams = methodInfo.GetParameters()\n    .Count(p => p.GetCustomAttributes(typeof(FromBodyAttribute), false).Any()) > 1;","typeGuard":null,"tryCatchPattern":"try { document = generator.Generate(settings); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"more than one body parameter\"))\n{\n    logger.LogError(ex, \"Operation has multiple body parameters\");\n}","preventionTips":["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."],"tags":["aspnetcore","swagger","body-parameter","openapi"],"backgroundTag":"multiple-body-parameters","analyzedSha":"63daf8fcc3a25151b62eb4b326a1e8ea048a0d41","analyzedAt":"2026-09-14T11:38:15.205Z","contentChangedAt":"2026-09-14T11:38:15.205Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}