OpenAPI errors

How to fix multipart/form-data file upload errors in OpenAPI codegen

A multipart/form-data file field with no format: binary on its schema is valid OpenAPI, but it generates a client that types the file parameter as a plain string and sends the whole request body as URL-encoded form data instead of FormData — so the upload either fails at compile time (wrong type) or ships bytes the server can't parse correctly. Reproduced with a real generator 2026-09-21; the fix is one keyword: format: binary.

What does the broken client actually generate?

Take a multipart upload endpoint where the file field is missing format: binary:

openapi: 3.0.3
info:
  title: Demo API
  version: 1.0.0
paths:
  /uploads:
    post:
      operationId: uploadFile
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                description:
                  type: string
      responses:
        "200":
          description: OK

Running this through OpenAPI Generator v7.25.0 (-g typescript-fetch) produces this real, unaltered output. First, the request parameter type:

export interface UploadFileRequest {
    file?: string;
    description?: string;
}

The file field is typed as string — there's no way to pass a Blob or a File object into it without a TypeScript type error at the call site. Second, and more damaging, the actual request-building logic:

const consumes: runtime.Consume[] = [
    { contentType: 'multipart/form-data' },
];
const canConsumeForm = runtime.canConsumeForm(consumes);

let formParams: { append(param: string, value: any): any };
let useForm = false;
if (useForm) {
    formParams = new FormData();
} else {
    formParams = new URLSearchParams();
}

useForm is hardcoded to false and the canConsumeForm check the generator computed is never actually used to set it. The request body ends up built with URLSearchParams, not FormData — which means the request is sent as application/x-www-form-urlencoded, not the multipart body a server-side file-upload handler expects, regardless of what the Content-Type header claims.

Why does one missing keyword cause this?

Because the generator's multipart-vs-form-urlencoded branch is driven by whether it detects an actual binary/file field in the schema, not just by the multipart/form-data content type key in the spec. type: string alone looks, to the generator, like an ordinary text form field — indistinguishable from description in the example above. Per the OpenAPI 3.0 specification's multipart guidance: "if the property is a type: string with format: binary... (aka a file object), the default Content-Type is application/octet-stream." format: binary is the signal the spec itself defines for "this string property is actually file content" — without it, a property that's semantically a file upload is indistinguishable, at the schema level, from a text field that happens to hold a filename.

What changes when you add format: binary?

Re-running the identical spec with format: binary added to the file property (and required: [file], since an upload endpoint should require the file) produces different, correct output. The request parameter type:

export interface UploadFileRequest {
    file: Blob;
    description?: string;
}

file is now typed as Blob — a real binary type — and required, matching what an upload endpoint actually needs. The request-building logic changes too:

const canConsumeForm = runtime.canConsumeForm(consumes);

let formParams: { append(param: string, value: any): any };
let useForm = false;
// use FormData to transmit files using content-type "multipart/form-data"
useForm = canConsumeForm;
if (useForm) {
    formParams = new FormData();
} else {
    formParams = new URLSearchParams();
}

useForm is now actually assigned from canConsumeForm instead of staying hardcoded false, so the branch takes the FormData path — the correct one for an actual file upload. Same generator, same generator version, same content type in the spec; the only difference between the broken and working output is format: binary on one property.

How do you fix it?

Add format: binary to the file property and mark it required:

# Before — file typed as string, request sent as URLSearchParams
requestBody:
  required: true
  content:
    multipart/form-data:
      schema:
        type: object
        properties:
          file:
            type: string
          description:
            type: string

# After — file typed as Blob, request correctly sent as FormData
requestBody:
  required: true
  content:
    multipart/form-data:
      schema:
        type: object
        properties:
          file:
            type: string
            format: binary
          description:
            type: string
        required:
          - file

If a file field can carry base64-encoded content instead of raw binary, use format: base64 — the same spec section covers both, and generators that support one binary format generally support both.

Does this affect every generator the same way?

The specific symptom (hardcoded useForm = false) is this generator's implementation of the underlying gap, but the root cause — a client that can't tell a file field from a text field without format: binary — is general across the ecosystem, because the OpenAPI spec text itself defines format: binary as the signal. Any generator or hand-written client that branches on "is this a file" rather than "is the content type multipart" is exposed to the same class of bug if the schema doesn't say so explicitly.

How to catch this before it ships

  1. Grep your spec for multipart/form-data content blocks and check every property inside them has an explicit type and, for file fields, format: binary or format: base64 — a bare type: string on what's meant to be a file is the exact gap shown above.
  2. Actually inspect the generated request-building code for a multipart endpoint at least once per generator/version — a passing build tells you the types compiled, not that the runtime request is shaped correctly.
  3. Run the spec through the free in-browser OpenAPI validator to catch missing or mismatched format on binary content before you generate a client from it.

If your API accepts file uploads and you're generating a TypeScript or Python client from the spec, Sourced's hosted docs and SDK pipeline previews the generated request types — including whether a file field actually typed as binary content — before you publish. Start a free report.

FAQ

Do I need format: binary, or is type: string with a description enough?

You need format: binary (or format: base64 for base64-encoded content). type: string alone is spec-valid but generic — it tells a generator nothing about whether the field holds file bytes or ordinary text, which is exactly the ambiguity that produces the broken client shown above.

Does multipart/form-data always mean file upload in OpenAPI?

No — multipart/form-data is a content type that can carry any mix of text fields and binary fields in the same request body. Not every property inside a multipart schema needs format: binary; only the ones that actually represent file content do.

Why did the build succeed if the generated code was broken?

Because a missing format: binary produces spec-valid OpenAPI and generator output that compiles — file?: string is a perfectly legal TypeScript type. The bug is behavioral (the request is sent with the wrong encoding), not a type error, so it only surfaces when you actually attempt an upload against a real server.

Can I upload multiple files in one field?

Yes — use an array schema: type: array, items: { type: string, format: binary }. Per the OpenAPI multipart guidance, an array of binary-format strings represents multiple files sent under the same form field name.

Should file uploads always use multipart/form-data instead of a raw binary body?

Use multipart/form-data when the file needs to travel alongside other form fields (like description in the example above) or as multiple files. For a single file with no accompanying metadata, a raw application/octet-stream request body with a type: string, format: binary schema is simpler and avoids multipart parsing entirely.

Does this bug affect downloads (responses) too, or only uploads (requests)?

The examples here are all requests, which is where the format: binary gap causes broken client code. Response bodies with binary content (file downloads) use the same format: binary convention, but most HTTP client libraries handle a binary response body correctly regardless of the schema's format, since the client just reads the raw response stream — the codegen-level bug shown here is specific to how the request is assembled.