Skip to content

P0: response-side tagged values are unspecified and silently lose data #2

Description

@ssilvius

Severity: P0. Reviewed at 82b715c. A conforming server can silently corrupt a value on the way out, and the conformance suite cannot catch it.

The hole

S5 defines the tagged-value encoding ({"$type":"bigint","$value":"<digits>"}) and puts exactly one obligation on the server: "Servers MUST accept the registered types" (SPEC.md:97). That is inbound only.

On the response side, S6.1 (SPEC.md:118) says row values "use the same JSON / tagged-value encoding as section 5." That names an encoding. It does not oblige anyone to emit it.

So a conforming server may return a stored SQLite INTEGER above 2^53 as a bare JSON number, and a JavaScript client rounds it. No rule is broken.

Why the conformance suite cannot catch it

P-5 roundtrips a bigint the client sent. A server that echoes what it was handed passes P-5 while still rounding every large value that was already in the table. The test needs to read a large integer the client never sent -- insert via literal SQL text, then SELECT it back.

Why a spec edit alone does not close it

This is the part that matters for tracking. encodeValue in both Cloudflare examples keys on the JS runtime type the driver handed back:

typeof value === "bigint"

By the time encodeValue sees a number, the driver has already rounded it -- the original 64-bit value is gone and is not recoverable at the encoding layer. So the fix is spec text PLUS driver configuration (D1/DO must be asked for bigints rather than numbers where the API allows it).

Do not let this issue be closed by an implementation that still loses the value. That is the specific failure mode worth guarding against here.

Proposed resolution

  • S6.1: make emission normative -- a server MUST emit the tagged form for values outside the JSON-safe range, not merely reference the S5 encoding.
  • Same treatment for BLOB values on the response side.
  • Add a conformance case that inserts a large integer as SQL literal text and asserts the tagged form comes back.

Acceptance

  • S6.1 puts a MUST on tagged-value emission, not just a cross-reference to S5.
  • New conformance case: value never sent by the client roundtrips without loss.
  • Both reference servers updated at the driver-config layer, and verified against the new case -- not just at encodeValue.

Related: lastInsertId is the same data-loss class, filed separately.

Origin: codex spec review; source-level sharpening (the encodeValue recovery point) by smugglr.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions