Skip to content

fix(#7): pin 415 + register unsupported_media_type for non-JSON Content-Type - #20

Merged
ssilvius merged 2 commits into
mainfrom
fix/7-unsupported-media-type
Aug 3, 2026
Merged

fix(#7): pin 415 + register unsupported_media_type for non-JSON Content-Type#20
ssilvius merged 2 commits into
mainfrom
fix/7-unsupported-media-type

Conversation

@ssilvius

@ssilvius ssilvius commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Summary

http-sql previously said nothing about what a server does when a client posts a body under the wrong Content-Type, so the conformance suite hedged with "400 or 415" and no registered error code covered the condition. This change pins the answer: section 2 requires HTTP 415 with error.code of unsupported_media_type, section 7 registers that code, conformance R-4 expects 415 specifically, and all three example servers emit it. The spec is explicit that only the media type is significant, so application/json; charset=utf-8 still executes normally -- a new R-5 case guards that, because a suite testing only rejection would pass an implementation that breaks ordinary clients. Versioning: issue #7 claims S11 makes this a v0.1.x-additive change. It does not. S11 says verbatim: "This spec uses <major>.<minor> versioning... Additive changes (new optional fields, new registered types, new registered error codes) increment the minor version." A new registered error code increments the minor version, and the scheme has no patch level, so v0.1.x is not a version this spec can express -- the code is v0.2. The text lands with a v0.2 marker rather than a document-wide renumber; see "Not done".

Acceptance criteria mapping

1. S2 states the required response for a non-JSON Content-Type: 415 with error.code = unsupported_media_type

Section 2 gained a normative paragraph after the accepted-method list, phrased in the house pattern already used by sections 3 and 4 ("MUST be rejected with the response defined in section 7 with error.code of ... and HTTP status ..."). It scopes the rule to the media type rather than the raw header value, and states that parameters such as charset are ignored, so the requirement cannot be read as forbidding application/json; charset=utf-8. Section 10.1 item 1 now points at section 2 rather than restating the rule, so the two cannot drift apart.

Evidence: SPEC.md section 2, the paragraph beginning "A request whose Content-Type media type is not application/json MUST be rejected"; and SPEC.md section 10.1 item 1.

2. unsupported_media_type added to the S7 registered code table with HTTP 415

The registered-code table gained one row, placed in the table's existing ascending-status order between payload_too_large (413) and rate_limited (429). The code column was widened uniformly so the new 22-character name does not leave a ragged table. A sentence directly under the table records that the code is introduced in v0.2 and cites section 11 as the reason -- this is the prominent versioning note the criterion's rationale got wrong.

Evidence: SPEC.md section 7, table row | unsupported_media_type| 415 | RequestContent-Typemedia type is notapplication/json. |, plus the paragraph immediately below it.

3. Conformance R-4 expects 415 specifically, not "400 or 415"

R-4's expected response is now 415, error.code=unsupported_media_type``, matching the cell format of its neighbours R-1..R-3. Because conformance/README.md frames itself as the v0.1 contract, R-4 carries a (v0.2+) marker and the intro defines that marker once, so a server advertising `X-Http-Sql-Version: 0.1` is not retroactively declared non-conforming. R-5 was added alongside it to pin the accepted case, `application/json; charset=utf-8`, which must execute normally.

Evidence: conformance/README.md, "Request shape" table rows R-4 and R-5, and the intro sentence beginning "Cases marked (v0.2+)".

4. Both reference servers emit it

Both Cloudflare examples now reject a non-JSON media type with 415 and the new code, and each does so at a point that preserves existing behavior: in the D1 Worker the check is inside the POST handler after the bearerAuth middleware, and in the Durable Object example it is in the router after the resolveTenant 401 -- so an unauthenticated request still gets 401 (conformance A-1) rather than 415, and a bad-media-type request never wakes a DO. Both paths run under the existing app.use("*") post-next() middleware, so the 415 still carries X-Http-Sql-Version and H-2 holds. The generic examples/reference-server.ts was updated too, since it is titled a reference http-sql server and would otherwise violate the MUST this change adds; it reuses its existing errorResponse helper, which already attaches the version header. All three share the same media-type predicate: lowercase, split on ;, trim, compare -- never exact header equality.

Evidence: isJsonMediaType in examples/cloudflare-worker-to-d1/src/index.ts, examples/cloudflare-durable-object/src/index.ts, and examples/reference-server.ts, each with a 415 return at its respective guard site.

Not done

The document is not renumbered to v0.2. X-Http-Sql-Version: 0.1 in section 9, conformance cases H-1/H-2, and the VERSION / VERSION_HEADER constants in the example servers are all untouched. Flipping the wire version is the one change that would invalidate every existing conformance claim in implementations.md, and cutting a release is a spec-owner decision, not something this issue authorizes -- so the new code is marked as v0.2 material and the actual version bump is left for whoever cuts 0.2. Also not done: the three example servers still duplicate the four-line isJsonMediaType helper rather than sharing a module, because each example is a standalone copy-paste reference with its own package.json; and the conformance TypeScript runner remains unimplemented, so R-4 and R-5 are contract text only, not executable tests.

Closes #7

…a_type

Section 2 now states the required response for a request whose Content-Type
media type is not application/json: HTTP 415 with error.code of
unsupported_media_type. Media-type parameters (charset) are explicitly
ignored. Section 7 registers the code; per section 11 a new registered error
code is additive and increments the minor version, so it is marked v0.2.

Conformance R-4 expects 415 specifically; R-5 pins the charset case. All three
example servers emit the new code before parsing the body and after auth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ssilvius
ssilvius merged commit 8239809 into main Aug 3, 2026
1 check passed
@ssilvius
ssilvius deleted the fix/7-unsupported-media-type branch August 3, 2026 05:28
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.

P2: no status pinned for wrong Content-Type -- pin 415, register unsupported_media_type

1 participant