{"record":{"id":"2464e1a827c3fae3","repo":"denoland/deno","slug":"missing-content-type","errorCode":null,"errorMessage":"Missing content type","messagePattern":"Missing content type","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"ext/fetch/22_body.js","lineNumber":472,"sourceCode":"          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;\n  let length = null;\n  let contentType = null;","sourceCodeStart":454,"sourceCodeEnd":490,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/fetch/22_body.js#L454-L490","documentation":"The null-mime branch of .formData() in ext/fetch/22_body.js: when no Content-Type header is present at all, MIME type parsing yields null and decoding form data is impossible - there is no way to pick a parser. The presence check happens after the multipart/urlencoded branches, so this fires only when the header is entirely absent (contrast with a wrong type, which is the previous error).","triggerScenarios":"await req.formData() on a request with no Content-Type header: fetch(url, { method: \"POST\", body }) without a headers object, or a server response that omits the header.","commonSituations":"Quick fetch() POSTs where the developer forgot headers entirely; proxies (or accidental middleware) stripping the Content-Type; 3rd-party endpoints that answer 200 without any Content-Type.","solutions":["Set an explicit Content-Type on the sending side: application/x-www-form-urlencoded for form posts","Guard the call: read the header first and only call formData() when it exists and names a form type","Treat a missing type defensively: reject with 400/415 rather than attempting form parsing"],"exampleFix":"// before (client)\nawait fetch(url, { method: \"POST\", body: new URLSearchParams({ a: 1 }) });\n\n// after (client)\nawait fetch(url, {\n  method: \"POST\",\n  headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n  body: new URLSearchParams({ a: 1 }),\n});","handlingStrategy":"validation","validationCode":"const ct = req.headers.get(\"content-type\");\nif (!ct) return new Response(\"content-type header required\", { status: 400 });\nconst form = await req.formData();","typeGuard":"function hasContentType(res: Response | Request): boolean {\n  return (res.headers.get(\"content-type\") ?? \"\").trim() !== \"\";\n}","tryCatchPattern":"try { return await req.formData(); } catch (e) {\n  if (e instanceof TypeError && e.message === \"Missing content type\") {\n    return new Response(\"content-type header required\", { status: 400 });\n  }\n  throw e;\n}","preventionTips":["Always send headers: { \"content-type\": ... } when POSTing bodies","Check for the header before calling formData() on foreign responses","In tests, mock fetch with realistic Content-Type headers"],"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"}