Skip to content

Pagination cursor's embedded sort field isn't cross-checked against the query's sort #119

Description

@cuibonobo

A cursor encodes the sort field it was minted under (field|value|id, cursor.ts:15). On decode, buildWhereClause uses the cursor's field for the keyset column but buildOrderClause uses the query's sort field (query.ts:139-147 vs 155-159). If a cursor minted under sort: createdAt is replayed against sort: version, the keyset predicate (on created_at) and the ORDER BY (on version) disagree — producing skipped or duplicated rows across the page boundary rather than an error.

This is a structurally-incoherent request, exactly the class decodeCursor already rejects for malformed/unknown-field cursors.

Fix

Cross-check the cursor's embedded field against the query's effective sort field (getSortField(query)); on mismatch throw StackQueryError (bad_request/400), consistent with the rest of the cursor codec. One comparison, at the point the cursor is decoded for the query.

Tests

  • A cursor minted under one sort field, replayed with a different sort.field, throws StackQueryError rather than returning an incoherent page
  • Same-field continuation still paginates correctly

Refs

#50 (cursor pagination), #53 (malformed-cursor → 400 bad_request). From docs/design-assessment-2026-07.md §F6.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions