OpenAPI errors

Fix Swagger 2.0 body/formData conversion errors in OpenAPI 3

Swagger 2.0's in: body and in: formData parameters don't exist in OpenAPI 3 — both collapse into a single requestBody object with a content map keyed by media type. Hand-converting them is where most migration bugs come from: a requestBody written without the content wrapper, or a leftover in: formData parameter, both validate as errors, not warnings, so they fail loud. The right fix is a real converter, not a find-and-replace. Reproduced with real validator output and a real swagger2openapi conversion on 2026-09-21.

What does a broken hand-conversion actually look like?

Start from a Swagger 2.0 operation with a JSON body parameter and a multipart upload with two formData parameters:

swagger: "2.0"
paths:
  /widgets:
    post:
      consumes: [application/json]
      parameters:
        - name: body
          in: body
          required: true
          schema:
            $ref: '#/definitions/Widget'
  /widgets/{id}/photo:
    post:
      consumes: [multipart/form-data]
      parameters:
        - name: file
          in: formData
          required: true
          type: file
        - name: caption
          in: formData
          required: false
          type: string

A common hand-conversion mistake is moving body's schema straight under a new requestBody key without wrapping it in content, and leaving formData parameters in the parameters array with in: formData unchanged (since that's the smallest edit that "looks" converted). Validating that version with swagger-cli validate v4.0.4 produces this real, unedited failure:

Swagger schema validation failed.
  #/paths/~1widgets/post/requestBody must have required property 'content'
  #/paths/~1widgets/post/requestBody must NOT have additional properties
  #/paths/~1widgets/post/requestBody must have required property '$ref'
  #/paths/~1widgets~1{id}~1photo/post/parameters/1 must have required property 'schema'
  #/paths/~1widgets~1{id}~1photo/post/parameters/1/in must be equal to one of the allowed values
  #/paths/~1widgets~1{id}~1photo/post/parameters/2 must have required property 'schema'
  #/paths/~1widgets~1{id}~1photo/post/parameters/2/in must be equal to one of the allowed values

Two distinct bugs in one output: requestBody needs a content map, not a bare schema, and formData is not a legal value for in in OpenAPI 3 at all — the Parameter Object only allows query, header, path, and cookie. Both body and formData parameters have to move into requestBody, not just get relabeled.

Why is this the part that breaks?

Because OpenAPI 3 restructured how a request payload is described, not just renamed a field. Swagger 2.0's Parameter Object states plainly: "since there can only be one payload, there can only be one body parameter," and separately, "form parameters... cannot be declared together with a body parameter for the same operation" — body and formData were always mutually exclusive alternate ways of describing the same conceptual thing: what's in the request payload. OpenAPI 3 made that explicit by giving the payload its own top-level requestBody object, keyed by media type under content, instead of parameters with special in values.

type: file for a formData parameter has no OpenAPI 3 equivalent either — it becomes type: string, format: binary inside the appropriate media type's schema. None of this is a rename; it's a real reshape from "some parameters happen to describe the body" to "the body is its own object with content-type-specific schemas," and every field that touched the old body/formData representation has to move, not just get patched in place.

How do you convert it correctly?

Use swagger2openapi — the reference converter that OpenAPI Generator and other tooling use internally — rather than hand-editing. Running the original, unmodified Swagger 2.0 spec through swagger2openapi v7.0.8 with --patch:

npx swagger2openapi spec.yaml -o converted.yaml --patch

produces this real, unedited result for both operations:

paths:
  /widgets:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/Widget"
        required: true
  "/widgets/{id}/photo":
    post:
      requestBody:
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                caption:
                  type: string
              required:
                - file
        required: true

The body parameter's schema moved under content.application/json, keyed by the operation's consumes value. The two formData parameters merged into a single multipart/form-data object schema, with file's type: file correctly converted to type: string, format: binary, and the required: true on the file parameter correctly folded into the object schema's required array instead of staying a parameter-level flag. Validating converted.yaml with swagger-cli validate passes clean, with no manual fixes.

Tool reaction table

Approach Result
Hand-edit: requestBody with bare schema, no content swagger-cli validate fails — missing content
Hand-edit: leftover in: formData parameters swagger-cli validate fails — invalid in value
swagger2openapi v7.0.8 --patch Correct requestBody.content for both JSON body and multipart form data; validates clean

How to convert Swagger 2.0 body/formData parameters the right way

  1. Run the whole document through swagger2openapi (npx swagger2openapi spec.yaml -o converted.yaml --patch) rather than editing parameters one at a time — it handles the bodyrequestBody.content move, the formData→multipart-object move, and the type: fileformat: binary conversion consistently, including edge cases like multiple consumes values producing multiple content media types.
  2. Validate the converted output, not just the original — swagger-cli validate or the free in-browser OpenAPI validator will catch a conversion that only partially completed, which is common if a spec mixes patterns across endpoints.
  3. Check every formData operation specifically for a leftover in: formData or in: body after conversion — these are the two values that don't exist in OpenAPI 3's Parameter Object at all, so any occurrence means the conversion missed an operation.
  4. Re-run your generator (OpenAPI Generator, or whichever SDK/docs pipeline you use) against the converted spec and diff the generated request-body handling against what customers currently depend on — a converted multipart/form-data schema changes how a generated SDK's method signature looks for file uploads specifically, which is worth a compatibility check with /openapi-diff/ before publishing.

If you haven't decided whether to upgrade off Swagger 2.0 at all, see OpenAPI 3.1 vs. Swagger 2.0: upgrade first and Swagger vs. OpenAPI in 2026 for the broader case before converting individual operations.

Honest scope

swagger2openapi handles the structural conversion covered here reliably and is the tool most OpenAPI-3-only pipelines already depend on internally — there's no reason to hand-roll this part. Where it can't help: deciding how to model a body/formData conversion that doesn't map cleanly, like an operation that historically accepted both a body parameter and query parameters together in ways Swagger 2.0 allowed loosely. Those need a human decision about the target shape, not just a converter.

If you're migrating a Swagger 2.0 API and want the converted spec to become hosted docs and a typed SDK in the same pass, Sourced's hosted docs and SDK pipeline takes the OpenAPI 3.0/3.1 output directly — run swagger2openapi first, then preview the result. Start a free report.

FAQ

Can OpenAPI 3 tooling read a Swagger 2.0 file directly, or do I always need to convert first?

Some tools, including OpenAPI Generator, accept Swagger 2.0 input directly and convert internally before generating. That's convenient for a one-off generation, but it means you never see the intermediate OpenAPI 3 document to validate or diff — converting explicitly with swagger2openapi first gives you a reviewable artifact and lets you catch a conversion problem before it's buried inside a codegen run.

Why does type: file become format: binary instead of a dedicated file type?

OpenAPI 3's Schema Object doesn't have a file type — JSON Schema, which the Schema Object is based on, never defined one. type: string, format: binary is the OpenAPI-specific convention for "this is binary data," and it's what every mainstream generator and the reference converter both target, so it's the correct and portable choice rather than a workaround.

Is --patch required, or does swagger2openapi work without it?

--patch tells swagger2openapi to fix a set of known common Swagger 2.0 spec mistakes during conversion (like certain malformed parameter definitions) rather than failing on them. For a spec that's already clean, it's harmless; for a spec migrated from an older or hand-maintained Swagger 2.0 document, it's usually the difference between a conversion that completes and one that errors out on a pre-existing issue unrelated to the body/formData change itself.

What happens to a body parameter's required field during conversion?

It maps directly to requestBody.required at the top level, as shown in the reproduced output above — that part is a straightforward move. The part that's easy to get wrong by hand is formData parameters' individual required flags, which don't map to a top-level flag at all; they fold into the required array of the generated object schema, one array shared across every formData parameter for that operation.

Do I need to change consumes and produces too?

Yes — Swagger 2.0's global and operation-level consumes/produces arrays don't exist in OpenAPI 3 at all. consumes becomes the media-type keys under requestBody.content (as shown above), and produces becomes the media-type keys under each response's content. swagger2openapi handles both automatically; a hand conversion that only touches parameters and forgets produces will leave every response without a usable content block.

Will the generated SDK's method signature change after converting a formData upload to requestBody?

Usually yes, in a good way — most generators produce a more explicit multipart-upload method (often accepting a Blob/File plus other fields as a typed object) from the converted requestBody.content['multipart/form-data'] shape than they did from loose formData parameters. If customers already depend on a published SDK's upload method signature, diff the generated client before and after with /openapi-diff/ so the change is intentional, not a surprise in a patch release.