Repository navigation
Serde and pagination: deserializeFrom abort, paginator leak on malformed PageInfo, tristate null cast, SSE double-reported release failure #79
Copy link
Copy link
Closed
Labels
area:coreCore HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*area:serdeSerialization and deserialization: SERDE-*Serialization and deserialization: SERDE-*area:streamingServer-sent events and pagination: SSE-* PAGE-*Server-sent events and pagination: SSE-* PAGE-*type:bugDefect in shipped behaviorDefect in shipped behavior
Milestone
Description
Part of the audit remediation umbrella #67. Milestone 4. Severity: MEDIUM. Requirements: SERDE-3, SERDE-14, PAGE-2, PAGE-27, SSE-30.
Current SDK behavior
{signal}cannot interrupt a pending read.deserializeFrom
(packages/codec-json/src/json-serde.ts:219-225) checks the signal before the lock and after
each chunk. Anawait reader.read()that never resolves is not raced against the signal. A
stream that enqueues{"a":and stalls, with a signal aborted 20 ms later, never settles and
leavessource.locked === true.seams/serde.ts:210-212and
docs/sdk-documentation/write-a-serde.md:118promise "an aborted call never leaves the
caller's source locked".serializeTo(serde.ts:98) has the same shape. The only tests use
already-aborted signals;tests/node-conformance/serde.test.mjshas no{signal}or
admitsNullcase.- A malformed
PageInfoleaks the response.paginator.ts:180clearsheldbefore
transport.send. Ifparseresolves toundefinedor{items: null}, the invariant at
:203or thePageconstructor at:212throws whileheld === undefined, so thefinally
at:221closes nothing.parseOrClose(:231-250) covers parse-throws only. Both
invariant messages (paginator.ts:207-210,page.ts:74-77) say "never null" but test
!== undefined;items: nullthen throws a bareTypeErrorfrom the spread atpage.ts:81. tristate()can produce a presentnull.tristate-schema.ts:45does
present(inner.parse(input) as NonNullable<T>). An inner schema that normalizes tonull
yields{kind: 'present', value: null}, the fourth state SERDE-14 forbids.- A release failure during an in-flight SSE error is reported twice.
sse/stream.ts:135,160-176:
the close error is suppressed onto the thrownSuppressedErrorand also passed to
onReleaseFailure. SSE-30 scopes the hook to the automatic clean-terminal path with no error
in flight.
Expected behavior
An aborted deserializeFrom or serializeTo settles promptly and leaves the caller's stream
unlocked. The paginator closes the response on every exit path, including a strategy that
returns a malformed result, and names the invariant it violated. tristate() never returns a
present null. A release failure is reported once: on the thrown error when an error is in
flight, through onReleaseFailure otherwise.
Notes and leads
- Abort: race
reader.read()against the signal (Promise.racewith an abort listener, removed
infinally), thenreleaseLock()as today. If the race is not wanted, narrow the doc to
"checked between chunks" instead. Mid-drain test injson-serde.test.tsand in
tests/node-conformance/serde.test.mjs. - Paginator: wrap the two invariants in the same close-then-rethrow discipline as
parseOrClose; test== nullto catch both. Tests:parse → undefined,parse → {items: null},
assertclosewas called once. tristate: runtime check afterinner.parse; throwDeserializationError.- SSE: skip the hook in
#releaseWithInFlightError. - Patch changesets for
@dexpace/coreand@dexpace/codec-json.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:coreCore HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*area:serdeSerialization and deserialization: SERDE-*Serialization and deserialization: SERDE-*area:streamingServer-sent events and pagination: SSE-* PAGE-*Server-sent events and pagination: SSE-* PAGE-*type:bugDefect in shipped behaviorDefect in shipped behavior
Activity
docs(core): the shipped .d.ts names the splice site and the abort race
docs: a paging strategy owes a well-formed PageInfo (PAGE-4)
Wahbeh-Mohammad commentedon Sep 5, 2026
Addressed in PR #94, merged into
audit/remediation-67; reachesmvpthrough PR #98. Decision record:docs/audit-67-decisions.md.