Skip to content

[Detail Bug] CLI: Filtered scans/bugs list can show impossible pagination (page > total pages) #328

Description

@detail-app

Detail Bug Report

https://app.detail.dev/org_5f375fe3-a706-4e9a-a6f7-800f2439b3f6/bugs/bug_b524d258-a726-4b84-987a-1d2c5774cfd5

Introduced in #282 by @sachiniyer on May 6, 2026

Summary

  • Context: The scans list command with filters (--status, --scan-type, --since, --until) performs client-side pagination after filtering
  • Bug: Requested page number is not validated against filtered result count, allowing impossible pagination state where page number exceeds total pages
  • Actual vs. expected: When filtering reduces total pages below requested page, displays "Page: X of Y" where X > Y. Should clamp to valid range or show page Y
  • Impact: Confusing user experience with empty results showing impossible pagination like "Page: 5 of 1"

Code with Bug

// src/commands/scans.rs lines 150-161
if needs_full_fetch {
    let all = fetch_all_scans(&client, &repo_id).await?;
    let filtered = filter_scans(
        &all,
        status.as_ref(),
        scan_type.as_ref(),
        since_ms,
        until_ms,
    );
    let total = filtered.len();
    let page_items = paginate_items(&filtered, *page, *limit);  // <-- BUG 🔴 page not validated against filtered total
    output_list(&page_items, total, *page, *limit, format)
}
// src/commands/bugs.rs lines 577-578
let page_items = paginate_items(&filtered, *page, *limit); // <-- BUG 🔴 same missing page validation
output_list(&page_items, total, *page, *limit, format)

Explanation

When filters are used, the CLI fetches all items, filters them client-side, then paginates the filtered list. If the user requests a page that was valid before filtering (e.g., page 2 of all results) but the filter reduces the result set to fewer pages (e.g., only 1 page), paginate_items uses the requested page to compute an offset beyond the end of the filtered list, returning an empty set. output_list still prints pagination metadata based on the filtered total, producing an impossible state like "Page: 2 of 1".

Codebase Inconsistency

The unfiltered path uses server-side pagination:

let scans = client.list_scans(&repo_id, *limit, offset).await?;
output_list(&scans.scans, usize::try_from(scans.total.max(0)).unwrap_or(0), *page, *limit, format)

The API returns a consistent total for the query and does not produce "page > total pages" states, while the filtered (client-side) path does.

Recommended Fix

Before calling paginate_items/output_list on the filtered path, clamp the requested page to the maximum page implied by the filtered total and limit (preferably by reusing the existing total_pages logic in src/output.rs). Apply the same change to scans list and bugs list.

History

This bug was introduced in commit 6ab434c. The commit added client-side filtering support (--status, --scan-type, --since, --until) to the scans list command. Since the scans API lacks server-side filtering, the implementation fetches all scans, filters them client-side, then re-paginates. The bug slipped in because the re-pagination logic at line 161 (paginate_items(&filtered, *page, *limit)) didn't validate that the requested page number was valid for the filtered result size, creating the impossible pagination state where page number exceeds total pages.

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