{"record":{"id":"c8de89cfa50949e6","repo":"Hmbown/CodeWhale","slug":"expected-application-x-www-form-urlencoded","errorCode":null,"errorMessage":"expected application/x-www-form-urlencoded","messagePattern":"expected application/x-www-form-urlencoded","errorType":"http","errorClass":"FormBodyError","httpStatus":415,"severity":"error","filePath":"web/lib/bounded-form.ts","lineNumber":19,"sourceCode":"import { BodyReadError, readBoundedBody } from \"./bounded-body\";\n\nexport class FormBodyError extends Error {\n  constructor(\n    readonly status: 400 | 413 | 415,\n    message: string\n  ) {\n    super(message);\n    this.name = \"FormBodyError\";\n  }\n}\n\nexport async function readBoundedUrlEncodedForm(\n  request: Request,\n  maxBytes: number\n): Promise<URLSearchParams> {\n  const mediaType = request.headers.get(\"content-type\")?.split(\";\", 1)[0]?.trim().toLowerCase();\n  if (mediaType !== \"application/x-www-form-urlencoded\") {\n    throw new FormBodyError(415, \"expected application/x-www-form-urlencoded\");\n  }\n\n  try {\n    const bytes = await readBoundedBody(request, maxBytes);\n    return new URLSearchParams(new TextDecoder().decode(bytes));\n  } catch (cause) {\n    if (cause instanceof BodyReadError) throw new FormBodyError(cause.status, cause.message);\n    throw cause;\n  }\n}\n","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/web/lib/bounded-form.ts#L1-L30","documentation":"readBoundedUrlEncodedForm only accepts bodies whose Content-Type media type (before parameters) is exactly application/x-www-form-urlencoded. Anything else throws FormBodyError with status 415 before reading the body.","triggerScenarios":"POST/form calls to a route using this helper while sending content-type application/json, multipart/form-data, text/plain, or a missing content-type header.","commonSituations":"A frontend switching from form submits to fetch() with a JSON body; an API client posting multipart data; scripts posting with curl without an explicit content type matching the form encoding.","solutions":["Send the body as application/x-www-form-urlencoded: `curl -d 'token=...'` or set the header explicitly.","For JSON or multipart payloads, use a route/helper that accepts that media type instead.","If using fetch, set `Content-Type: application/x-www-form-urlencoded` and encode the body with URLSearchParams."],"exampleFix":"// before\nawait fetch(url, { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify(data) });\n// after\nawait fetch(url, { method: \"POST\", headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" }, body: new URLSearchParams(data) });","handlingStrategy":"validation","validationCode":"const mt = request.headers.get(\"content-type\")?.split(\";\")[0]?.trim().toLowerCase();\nif (mt !== \"application/x-www-form-urlencoded\") throw new Error(`wrong media type: ${mt}`);","typeGuard":null,"tryCatchPattern":"try {\n  const form = await readBoundedUrlEncodedForm(request, MAX);\n} catch (err) {\n  if (err instanceof FormBodyError && err.status === 415) {\n    return new Response(\"send application/x-www-form-urlencoded\", { status: 415 });\n  }\n  throw err;\n}","preventionTips":["Use `new URLSearchParams(obj)` as the fetch body — it sets the correct content type automatically.","Never override the Content-Type header when submitting URL-encoded forms.","Match the client encoding to the endpoint: JSON/multipart go to different routes."],"tags":["http","content-type","validation"],"backgroundTag":"unsupported-media-type","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}