{"record":{"id":"b60a7e46229c113d","repo":"denoland/deno","slug":"body-can-not-be-decoded-as-form-data","errorCode":null,"errorMessage":"Body can not be decoded as form data","messagePattern":"Body can not be decoded as form data","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/22_body.js","lineNumber":470,"sourceCode":"        if (essence === \"multipart/form-data\") {\n          const boundary = mimeType.parameters.get(\"boundary\");\n          if (boundary === null) {\n            throw new TypeError(\n              \"Cannot turn into form data: missing boundary parameter in mime type of multipart form data\",\n            );\n          }\n          return parseFormData(chunkToU8(bytes), boundary);\n        } else if (essence === \"application/x-www-form-urlencoded\") {\n          // TODO(@AaronO): pass as-is with StringOrBuffer in op-layer\n          const entries = parseUrlEncoded(chunkToU8(bytes));\n          return formDataFromEntries(\n            ArrayPrototypeMap(\n              entries,\n              (x) => ({ name: x[0], value: x[1] }),\n            ),\n          );\n        }\n        throw new TypeError(\"Body can not be decoded as form data\");\n      }\n      throw new TypeError(\"Missing content type\");\n    }\n    case \"JSON\":\n      return JSONParse(chunkToString(bytes));\n    case \"text\":\n      return chunkToString(bytes);\n  }\n}\n\n/**\n * @param {BodyInit} object\n * @returns {{body: InnerBody, contentType: string | null}}\n */\nfunction extractBody(object) {\n  /** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */\n  let stream;\n  let source = null;","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/22_body.js#L452-L488","documentation":"In packageBytes, .formData() only decodes two content types: multipart/form-data and application/x-www-form-urlencoded. If a Content-Type is present but its essence is anything else (text/plain, application/json, ...), parsing stops with this TypeError - the body will not be reinterpreted as form data.","triggerScenarios":"await req.formData() on a request posted with Content-Type: text/plain or application/json; senders that default to text/plain when no explicit type is set (older fetch or curl -d without --data-urlencode).","commonSituations":"Accepting form posts while the client library actually sends JSON; curl-based integrations that forget the content-type flag; endpoints that multiplex JSON and form data but always call formData().","solutions":["Branch on the Content-Type: JSON.parse(await req.text()) for application/json, formData() only for the two form types","Fix the client to send application/x-www-form-urlencoded or multipart/form-data","Return 415 Unsupported Media Type when the type is not one your handler supports"],"exampleFix":"// before\nconst form = await req.formData(); // client sent application/json -> throws\n\n// after\nconst ct = req.headers.get(\"content-type\") ?? \"\";\nif (ct.includes(\"application/json\")) {\n  const data = await req.json(); /* ... */\n} else if (ct.includes(\"form-data\") || ct.includes(\"urlencoded\")) {\n  const form = await req.formData(); /* ... */\n} else {\n  return new Response(\"unsupported media type\", { status: 415 });\n}","handlingStrategy":"validation","validationCode":"const ct = req.headers.get(\"content-type\") ?? \"\";\nif (!/multipart\\/form-data|application\\/x-www-form-urlencoded/i.test(ct)) {\n  return new Response(\"expected form data\", { status: 415 });\n}\nconst form = await req.formData();","typeGuard":"function isFormDataContentType(ct: string | null): boolean {\n  if (ct == null) return false;\n  const essence = ct.split(\";\")[0].trim().toLowerCase();\n  return essence === \"multipart/form-data\" || essence === \"application/x-www-form-urlencoded\";\n}","tryCatchPattern":"try { return await req.formData(); } catch (e) {\n  if (e instanceof TypeError && e.message.includes(\"can not be decoded as form data\")) {\n    return new Response(\"send multipart/form-data or x-www-form-urlencoded\", { status: 415 });\n  }\n  throw e;\n}","preventionTips":["Dispatch parsers by Content-Type essence, not by endpoint convention","Set the content-type explicitly on every form-posting client","Reject unsupported media types with 415 before attempting to parse"],"tags":["fetch","form-data","http","content-type"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}