{"record":{"id":"384b8d65a602bc7a","repo":"withastro/astro","slug":"unsupported-content-type","errorCode":null,"errorMessage":"Unsupported content type","messagePattern":"Unsupported content type","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/astro/src/actions/runtime/server.ts","lineNumber":302,"sourceCode":"\t\tif (hasContentType(contentType, ['application/json'])) {\n\t\t\tif (contentLength === 0) return undefined;\n\t\t\tif (!hasContentLength) {\n\t\t\t\tconst body = await readBodyWithLimit(request.clone(), bodySizeLimit);\n\t\t\t\tif (body.byteLength === 0) return undefined;\n\t\t\t\treturn JSON.parse(new TextDecoder().decode(body));\n\t\t\t}\n\t\t\treturn await request.clone().json();\n\t\t}\n\t} catch (e) {\n\t\tif (e instanceof BodySizeLimitError) {\n\t\t\tthrow new ActionError({\n\t\t\t\tcode: 'CONTENT_TOO_LARGE',\n\t\t\t\tmessage: `Request body exceeds ${bodySizeLimit} bytes`,\n\t\t\t});\n\t\t}\n\t\tthrow e;\n\t}\n\tthrow new TypeError('Unsupported content type');\n}\n\nexport const ACTION_API_CONTEXT_SYMBOL = Symbol.for('astro.actionAPIContext');\n\nconst formContentTypes = ['application/x-www-form-urlencoded', 'multipart/form-data'];\n\nfunction hasContentType(contentType: string, expected: string[]) {\n\t// Split off parameters like charset or boundary\n\t// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type#content-type_in_html_forms\n\tconst type = contentType.split(';')[0].toLowerCase();\n\n\treturn expected.some((t) => type === t);\n}\n\nfunction isActionAPIContext(ctx: ActionAPIContext): boolean {\n\tconst symbol = Reflect.get(ctx, ACTION_API_CONTEXT_SYMBOL);\n\treturn symbol === true;\n}","sourceCodeStart":284,"sourceCodeEnd":320,"githubUrl":"https://github.com/withastro/astro/blob/d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca/packages/astro/src/actions/runtime/server.ts#L284-L320","documentation":"parseRequestBody only recognizes form content types (application/x-www-form-urlencoded, multipart/form-data) and application/json. Any other Content-Type falls through to throw new TypeError('Unsupported content type').","triggerScenarios":"An action request whose Content-Type is text/plain, application/xml, a malformed multipart without a valid boundary, or any unrecognized type.","commonSituations":"A custom fetch that forgets to set Content-Type; a client sending text/plain; a proxy rewriting the header; a corrupted multipart boundary.","solutions":["Send application/json for JSON actions (let the client proxy set it).","For form posts, ensure a proper form Content-Type with a valid boundary.","Verify the request Content-Type header on the client before sending."],"exampleFix":"// before - manual fetch with wrong header\nawait fetch(actionUrl, { method: 'POST', headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify(data) });\n// after\nawait fetch(actionUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });","handlingStrategy":"validation","validationCode":"// Only send recognized content types to action requests.\nconst ALLOWED = new Set(['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data']);\nconst ct = headers.get('Content-Type')?.split(';')[0].trim().toLowerCase();\nif (!ct || !ALLOWED.has(ct)) throw new Error(`Unsupported Content-Type: ${ct}`);","typeGuard":"function isSupportedContentType(ct: string): boolean {\n  const base = ct.split(';')[0].trim().toLowerCase();\n  return ['application/json', 'application/x-www-form-urlencoded', 'multipart/form-data'].includes(base);\n}","tryCatchPattern":"try {\n  await actions.myAction(payload);\n} catch (e) {\n  if (e instanceof TypeError && /Unsupported content type/i.test(e.message)) {\n    // set a proper Content-Type and retry\n  } else throw e;\n}","preventionTips":["Let the actions client proxy set Content-Type instead of setting it manually.","Use application/json for JSON actions and real form types for form actions.","Validate the Content-Type header before custom fetches."],"tags":["actions","content-type","http"],"backgroundTag":null,"analyzedSha":"d081033d5fe8e8a68c4bbbad4af9d2deb9c74bca","analyzedAt":"2026-08-12T13:37:29.035Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}