Skip to content

fix: answer unreadable request formats with 415, and count content types (DW-985) - #2083

Merged
Crown421 merged 1 commit into
mainfrom
steffen/dw-985-return-415-for-unreadable-content
Oct 22, 2026
Merged

Crown421 merged 1 commit into
mainfrom
steffen/dw-985-return-415-for-unreadable-content

Conversation

@Crown421

@Crown421 Crown421 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #2082. Completes DW-985.

Why

RFC 9110 §15.5.16 reserves 415 for a request whose content format or coding the server doesn't support. #2082 returns it for unsupported content codings. Three other refusals in the same class still came back as a 400, which tells the client its request was malformed rather than sent in the wrong format:

All three already fail today, so changing the status breaks no working client.

What changes

  • dwctl: a new Error::UnsupportedMediaType variant, returned as a 415.
  • File uploads: a request without a multipart/form-data content type gets a 415 before it takes an upload slot. A multipart request with a broken form, such as a bad boundary, is still a 400. Files compressed in a format we don't decode also get a 415.
  • onwards, strict /responses: a missing JSON content type returns 415. The code stays invalid_content_type, so onwards_rejections_total series keep their label.
  • New metric: dwctl_inference_request_content_types_total{content_type} counts the content type declared on each POST to the inference endpoints, i.e. everything under /ai/v1/* except the file and batch routes, which already check theirs. Values are grouped into json, form, multipart, text, other and none. /chat/completions, /completions and /embeddings parse the body whatever it declares, and curl -d without -H declares a form body. Rejecting on content type there could break working clients, so this measures how many such requests there are before anyone decides.

Not in this PR

  • Rejecting non-JSON content types on the lenient endpoints: a follow-up, once the new metric shows how much traffic it would affect.
  • Non-UTF-8 charset parameters: deliberately left alone for the same reason.

Testing

  • test_upload_that_is_not_a_multipart_form_returns_415: a JSONL body, a form body and a missing content type each get a 415. A multipart body with a broken form still gets a 400.
  • test_upload_broken_or_unsupported_compression_names_the_problem: xz gets a 415, and truncated gzip still gets a 400.
  • responses_bodies_not_sent_as_json_are_counted_as_such in onwards: now asserts a 415 and the unchanged rejection code.
  • classifies_declared_content_types: covers the metric's labels.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Deploying control-layer with  Cloudflare Pages  Cloudflare Pages

Latest commit: 91e6ff2
Status: ✅  Deploy successful!
Preview URL: https://9894111e.control-layer.pages.dev
Branch Preview URL: https://steffen-dw-985-return-415-fo.control-layer.pages.dev

View logs

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 5 files

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread dwctl/src/inference/body_limit.rs
@Crown421
Crown421 force-pushed the steffen/dw-985-return-415-for-unreadable-content branch 2 times, most recently from 33b547c to f43ad20 Compare October 21, 2026 21:34
@pjb157
pjb157 added this pull request to stack #2088 October 22, 2026 11:05
Copilot AI balanced review requested due to automatic review settings October 22, 2026 11:06
@Crown421
Crown421 force-pushed the steffen/dw-985-return-415-for-unreadable-content branch from f43ad20 to 457e752 Compare October 22, 2026 11:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing. Review the Copilot code review billing settings.

Base automatically changed from steffen/dw-985-accept-compressed-request-bodies-and-return-415-for-content to main October 22, 2026 11:14
…pes (DW-985)

RFC 9110 reserves 415 for a body in a format or coding the server doesn't
support. Non-multipart file uploads, batch files compressed with xz, zip,
bzip2 or lz4, and strict /responses bodies not declared as JSON were 400s,
telling clients their request was malformed rather than sent in the wrong
format. All three already failed, so no working client changes behaviour.

The lenient JSON endpoints parse whatever content type a client declares,
and curl -d declares a form body. Count the declared content types before
deciding whether rejecting any of them is safe.
@Crown421
Crown421 force-pushed the steffen/dw-985-return-415-for-unreadable-content branch from 457e752 to 91e6ff2 Compare October 22, 2026 11:15
@Crown421
Crown421 added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit 58e3a52 Oct 22, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants