fix(#7): pin 415 + register unsupported_media_type for non-JSON Content-Type - #20
Merged
Conversation
…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>
This was referenced Aug 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 witherror.codeofunsupported_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, soapplication/json; charset=utf-8still 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, sov0.1.xis 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 witherror.code=unsupported_media_typeSection 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.codeof ... and HTTP status ..."). It scopes the rule to the media type rather than the raw header value, and states that parameters such ascharsetare ignored, so the requirement cannot be read as forbiddingapplication/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-Typemedia type is notapplication/jsonMUST be rejected"; and SPEC.md section 10.1 item 1.2.
unsupported_media_typeadded to the S7 registered code table with HTTP 415The registered-code table gained one row, placed in the table's existing ascending-status order between
payload_too_large(413) andrate_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
resolveTenant401 -- 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 existingapp.use("*")post-next()middleware, so the 415 still carriesX-Http-Sql-Versionand H-2 holds. The genericexamples/reference-server.tswas updated too, since it is titled a reference http-sql server and would otherwise violate the MUST this change adds; it reuses its existingerrorResponsehelper, which already attaches the version header. All three share the same media-type predicate: lowercase, split on;, trim, compare -- never exact header equality.Evidence:
isJsonMediaTypein 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.1in section 9, conformance cases H-1/H-2, and theVERSION/VERSION_HEADERconstants 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-lineisJsonMediaTypehelper 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