Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions src/commands/dashboard/revisions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,13 @@
);

const trimmed = results.slice(0, flags.limit);
const hasMore = results.length > flags.limit || !!nextCursor;
const cursorToStore = hasMore ? nextCursor : undefined;
const overshot = results.length > flags.limit;
const hasMore = overshot || !!nextCursor;
// When multi-page fetch overshoots the limit, the API cursor points past
// trimmed items — storing it would skip them on '-c next'. Drop it to
// match autoPaginate() behavior (see infrastructure.ts lines 419-420).
const cursorToStore = !overshot && hasMore ? nextCursor : undefined;

Check warning on line 208 in src/commands/dashboard/revisions.ts

View check run for this annotation

@sentry/warden / warden: find-bugs

`hasMore = true` when overshot but no cursor stored causes `-c next` to throw a ValidationError

When the multi-page fetch overshoots `flags.limit`, `hasMore` is set to `true` and the `paginationHint` prints a `-c next` hint, but `cursorToStore` is `undefined`, so `advancePaginationState` stores no next entry in the stack. Running `--cursor next` then hits `state.index + 1 >= state.stack.length` in `resolveCursor` and throws `"No next page saved for this query"` — the opposite of what `hasMore: true` implies.
Comment on lines +202 to 208

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasMore = true when overshot but no cursor stored causes -c next to throw a ValidationError

When the multi-page fetch overshoots flags.limit, hasMore is set to true and the paginationHint prints a -c next hint, but cursorToStore is undefined, so advancePaginationState stores no next entry in the stack. Running --cursor next then hits state.index + 1 >= state.stack.length in resolveCursor and throws "No next page saved for this query" — the opposite of what hasMore: true implies.

Evidence
  • overshot = truecursorToStore = undefined (line 208); hasMore = true (line 203).
  • advancePaginationState(..., direction='first', undefined) stores stack = [''] with no next entry (pagination.ts:265: nextCursor ? ['', nextCursor] : ['']).
  • outputData.hasMore = true causes paginationHint to emit the -c next hint (revisions.ts line 229).
  • resolveCursor(..., 'next') in pagination.ts:198 checks state.index + 1 >= state.stack.length1 >= 1 → throws ValidationError('No next page saved for this query.').
  • autoPaginate() (infrastructure.ts line 419) never exposes hasMore to callers, so the cited comparison does not apply here where hasMore is surfaced directly in CLI output.

Identified by Warden find-bugs · LHQ-E68

advancePaginationState(
PAGINATION_KEY,
contextKey,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/api/logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export async function listLogs(
field: fields,
project: isNumericProject ? [Number(projectSlug)] : undefined,
query: fullQuery || undefined,
per_page: options.limit || API_MAX_PER_PAGE,
per_page: Math.min(options.limit || API_MAX_PER_PAGE, API_MAX_PER_PAGE),
statsPeriod:
options.start || options.end
? undefined
Expand Down Expand Up @@ -343,7 +343,7 @@ export async function listTraceLogs(
: (options.statsPeriod ?? "14d"),
start: options.start,
end: options.end,
per_page: options.limit ?? API_MAX_PER_PAGE,
per_page: Math.min(options.limit ?? API_MAX_PER_PAGE, API_MAX_PER_PAGE),
query: options.query,
sort: toApiSort(options.sort),
},
Expand Down
12 changes: 11 additions & 1 deletion src/lib/sourcemap/debug-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import { createHash } from "node:crypto";
import { readFile, writeFile } from "node:fs/promises";
import { ValidationError } from "../errors.js";
import { logger } from "../logger.js";
import {
type DecodedInlineMap,
Expand Down Expand Up @@ -152,7 +153,16 @@ export async function injectDebugId(
newJs += `\n${DEBUGID_COMMENT_PREFIX}${debugId}\n`;

// --- Mutate sourcemap ---
const map = JSON.parse(mapContent) as SourcemapJson;
let map: SourcemapJson;
try {
map = JSON.parse(mapContent) as SourcemapJson;
} catch (error) {
log.debug("Failed to parse sourcemap JSON", error);
throw new ValidationError(
`Failed to parse sourcemap ${mapPath}: file is not valid JSON`,
"mapPath"
);
}
mutateSourcemap(map, debugId, { offsetMappings: !skipSnippet });

// Write both files concurrently
Expand Down
Loading