Severity: P1. Reviewed at 82b715c. The spec qualifies a MUST with a mechanism that does not exist anywhere in the document.
The hole
S10.1 item 5 (SPEC.md:206), under "A v0.1 conforming server MUST":
Honor atomic: true on batch requests when not rejected.
There is no rejection mechanism for atomic:
- The S7 registered error code table has no
not_supported (or equivalent).
- S4.2's only rejection clause is statement-count overflow to
413 / payload_too_large -- which rejects the batch, not the atomicity.
So "when not rejected" is a qualifier pointing at nothing. A server that genuinely cannot do transactions has no conforming way to say so. It must either lie -- accept atomic: true and execute non-atomically -- or misuse an unrelated error code. A client cannot distinguish those two from success.
What this is not
Worth stating explicitly, because it is the first thing a reader assumes and it is wrong: atomic being OPTIONAL at SPEC.md:64 does not create the escape hatch. OPTIONAL there governs whether the field appears in the request. The same sentence puts a MUST on the server: "when true, the server MUST execute all statements in a single transaction."
Both reference servers honor it today, verified at 82b715c:
examples/cloudflare-worker-to-d1/src/index.ts -- the atomic branch prepares the whole batch and calls db.batch(), which is a transaction.
examples/cloudflare-durable-object/src/index.ts -- ctx.storage.transactionSync.
So this is not a case of servers declining in practice. It is a case of a stated escape hatch that no server can actually use.
Why it matters to a client
The consequence is undetectability, and it is stronger than "some servers might not support transactions." A client cannot confirm that a given target honored atomic, in any deployment, ever. Correctness therefore must not rest on a primitive whose presence is unobservable -- which is why a migration applier has to be correct without atomic even in a future where every server honors it perfectly.
That property is what makes this worth fixing rather than documenting.
Resolution -- pick one
- Register a rejection mechanism. Add
not_supported to the S7 table with a defined status, and state that a server unable to execute atomically MUST reject atomic: true with it rather than silently degrading. Preserves "when not rejected" and makes it mean something. Neither reference server changes -- both honor atomic, so neither ever emits it.
- Drop the qualifier. Make S10.1 item 5 an unconditional MUST. Simpler, and honest about what the spec already requires, but it leaves non-transactional backends with no conforming path at all.
Recommend (1): it costs one error code, and it converts a silent failure into a detectable one, which is the entire value at stake.
Acceptance
Generalizable tell
A conformance list that qualifies a MUST with a mechanism defined nowhere is a repeatable spec bug worth grepping for -- "when not rejected", "where supported", "if available". Each one needs the mechanism to exist.
Origin: platform, from a misread of this same passage by smugglr that inverted on inspection -- the conclusion (do not depend on atomic) survived, the reason changed from "servers may decline" to "no server can declare".
Severity: P1. Reviewed at 82b715c. The spec qualifies a MUST with a mechanism that does not exist anywhere in the document.
The hole
S10.1 item 5 (
SPEC.md:206), under "A v0.1 conforming server MUST":There is no rejection mechanism for
atomic:not_supported(or equivalent).413/payload_too_large-- which rejects the batch, not the atomicity.So "when not rejected" is a qualifier pointing at nothing. A server that genuinely cannot do transactions has no conforming way to say so. It must either lie -- accept
atomic: trueand execute non-atomically -- or misuse an unrelated error code. A client cannot distinguish those two from success.What this is not
Worth stating explicitly, because it is the first thing a reader assumes and it is wrong:
atomicbeing OPTIONAL atSPEC.md:64does not create the escape hatch. OPTIONAL there governs whether the field appears in the request. The same sentence puts a MUST on the server: "whentrue, the server MUST execute all statements in a single transaction."Both reference servers honor it today, verified at 82b715c:
examples/cloudflare-worker-to-d1/src/index.ts-- the atomic branch prepares the whole batch and callsdb.batch(), which is a transaction.examples/cloudflare-durable-object/src/index.ts--ctx.storage.transactionSync.So this is not a case of servers declining in practice. It is a case of a stated escape hatch that no server can actually use.
Why it matters to a client
The consequence is undetectability, and it is stronger than "some servers might not support transactions." A client cannot confirm that a given target honored
atomic, in any deployment, ever. Correctness therefore must not rest on a primitive whose presence is unobservable -- which is why a migration applier has to be correct withoutatomiceven in a future where every server honors it perfectly.That property is what makes this worth fixing rather than documenting.
Resolution -- pick one
not_supportedto the S7 table with a defined status, and state that a server unable to execute atomically MUST rejectatomic: truewith it rather than silently degrading. Preserves "when not rejected" and makes it mean something. Neither reference server changes -- both honor atomic, so neither ever emits it.Recommend (1): it costs one error code, and it converts a silent failure into a detectable one, which is the entire value at stake.
Acceptance
not_supportedregistered in S7; S4.2 states the reject-rather-than-degrade obligation; conformance case added.Generalizable tell
A conformance list that qualifies a MUST with a mechanism defined nowhere is a repeatable spec bug worth grepping for -- "when not rejected", "where supported", "if available". Each one needs the mechanism to exist.
Origin: platform, from a misread of this same passage by smugglr that inverted on inspection -- the conclusion (do not depend on
atomic) survived, the reason changed from "servers may decline" to "no server can declare".