AI Workshop: learn to build apps with AI →
Common Errors: FormData multipart fetch issues

Join the AI Workshop and learn to build real-world apps with AI. A hands-on, practical program to level up your skills.


When uploading files using the Fetch API with FormData, you might encounter issues where the file data isn’t actually sent to the server.

The problem

Here’s a typical setup that might fail:

<form
  encType='multipart/form-data'
  action='/api/post'
  method='POST'
  onSubmit={async (e) => {
    e.preventDefault()

    const body = new FormData()
    body.append('image', image)
    body.append('title', title)
    body.append('content', content)

    const res = await fetch('/api/post', {
      body,
      method: 'POST',
      headers: {
        'Content-Type': 'multipart/form-data',
      },
    })
  }}
>
...
</form>

The file data isn’t being sent to the server correctly.

The solution

Do not set the Content-Type header manually.

Omit the Content-Type header from the fetch request (or omit the entire headers object):

const res = await fetch('/api/post', {
  body,
  method: 'POST',
  // No headers!
})

Why this works

When you use FormData, the browser needs to:

  1. Set the Content-Type to multipart/form-data
  2. Add a boundary parameter that separates the different parts of the form data

When you manually set Content-Type: 'multipart/form-data', you override the browser’s header without the boundary. The server then can’t parse the data correctly.

By omitting the header, the browser automatically sets:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...

Correct example

const handleSubmit = async (e) => {
  e.preventDefault()

  const formData = new FormData()
  formData.append('image', imageFile)
  formData.append('title', title)
  formData.append('content', content)

  const response = await fetch('/api/upload', {
    method: 'POST',
    body: formData,
    // Let the browser set the Content-Type header automatically
  })

  const result = await response.json()
}

Key takeaway

When using FormData with fetch, let the browser handle the Content-Type header. This applies to any situation where you’re sending files or multipart data.

Lessons in this unit:

0: Introduction
1: "X is not a function" errors
2: "document is not defined" error
3: Cannot assign to read only property
4: Parse failure: Unterminated string constant
5: ▶︎ FormData multipart fetch issues
6: regeneratorRuntime is not defined
7: Unexpected identifier in modules
8: Unterminated string literal