Skip to content

fix(ui): stale filters applied after sort/page/time change on Request… - #25789

Merged
krrish-berri-2 merged 1 commit into
BerriAI:litellm_oss_staging_04_22_2026from
Bytechoreographer:fix/logs-stale-filters-on-sort-page-time
Apr 23, 2026
Merged

fix(ui): stale filters applied after sort/page/time change on Request…#25789
krrish-berri-2 merged 1 commit into
BerriAI:litellm_oss_staging_04_22_2026from
Bytechoreographer:fix/logs-stale-filters-on-sort-page-time

Conversation

@Bytechoreographer

Copy link
Copy Markdown
Contributor

PR 2: fix(ui): stale filters applied after sort/page/time change on Request Logs

Branch: Bytechoreographer:fix/logs-stale-filters-on-sort-page-time
Target: BerriAI:litellm_oss_branch
PR link: https://github.com/Bytechoreographer/litellm/pull/new/fix/logs-stale-filters-on-sort-page-time


Relevant issues

Pre-Submission checklist

  • npm run test passes for affected files (29/29)
  • Scope is isolated: one file changed, no new dependencies
  • Comment @greptileai and get Confidence Score ≥ 4/5 before requesting maintainer review

Type

🐛 Bug Fix

Changes

Root cause

useLogFilterLogic has a useEffect that re-fetches logs whenever sort,
page, or time range changes while backend filters are active:

useEffect(() => {
  if (hasBackendFilters && accessToken) {
    debouncedSearch.cancel();
    performSearch(filters, currentPage);   // ← stale closure!
  }
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [sortBy, sortOrder, currentPage, startTime, endTime, isCustomDate]);

filters and hasBackendFilters are intentionally omitted from the dep
array to prevent double-fetches when a filter is applied (filter changes are
handled by handleFilterChange → debouncedSearch).

The side-effect is a stale-closure bug: React captures filters and
hasBackendFilters from the render where the effect was last recreated —
i.e., when sortBy, sortOrder, currentPage, startTime, endTime, or
isCustomDate last changed. If the user sets a filter (e.g. Key Alias)
after that point, the effect still holds the old snapshot that predates
the filter selection.

Reproduce:

  1. Open Request Logs → set Key Alias filter → filtered results appear ✓
  2. Change page, sort column, or time range
  3. The effect fires with the stale filters (no key_alias) → API request
    sent without the filter → table shows unfiltered data ✗

This is why the filter appears to "sometimes work, sometimes not": the initial
debounce-triggered search uses the correct filters, but any subsequent
sort/page/time interaction resets the results.

Fix

Store the latest filters and hasBackendFilters in refs kept in sync on
every render. The sort/page/time effect reads from the refs instead of the
closure, so it always uses the current filter state without requiring
those values to be in the dep array:

// Always-current refs — updated every render
const filtersRef = useRef(filters);
const hasBackendFiltersRef = useRef(false);

useEffect(() => {
  filtersRef.current = filters;
  hasBackendFiltersRef.current = hasBackendFilters;
}, [filters, hasBackendFilters]);

// Sort/page/time effect now reads from refs → no stale closure
useEffect(() => {
  if (hasBackendFiltersRef.current && accessToken) {
    debouncedSearch.cancel();
    performSearch(filtersRef.current, currentPage);
  }
  // eslint-disable-next-line react-hooks/exhaustive-deps
}, [sortBy, sortOrder, currentPage, startTime, endTime, isCustomDate]);

Files changed

File Change
ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx Add filtersRef / hasBackendFiltersRef, sync effect, update sort/page/time effect to read from refs

… Logs

The useEffect that re-fetches logs on sort/page/time changes:

  useEffect(() => {
    if (hasBackendFilters && accessToken) {
      performSearch(filters, currentPage);
    }
  }, [sortBy, sortOrder, currentPage, startTime, endTime, isCustomDate]);

intentionally omits `filters` and `hasBackendFilters` from its dep array
to avoid double-fetches when a filter is applied.  The side-effect is a
stale-closure bug: the effect captures `filters` and `hasBackendFilters`
from the render where its deps last changed, not from the render where
the user selected, e.g., a Key Alias.

Reproduce: set Key Alias → results appear correctly → change page or
sort → the effect fires with the OLD `filters` snapshot (no key_alias)
→ API request is sent without the filter → table shows unfiltered data.

Fix: store the latest `filters` and `hasBackendFilters` in refs that are
kept in sync on every render.  The sort/page/time effect reads from the
refs instead of the closure so it always uses the current filter state
without altering the dep array.

Co-Authored-By: Claude Sonnet 4 (1M context) <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Apr 15, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@vercel

vercel Bot commented Apr 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 15, 2026 2:28pm

Request Review

@greptile-apps

greptile-apps Bot commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a stale-closure bug in useLogFilterLogic where the sort/page/time-change effect captured filters and hasBackendFilters from an outdated render snapshot, causing backend filters (e.g., Key Alias) to be silently dropped on the next sort or page interaction.

The fix introduces filtersRef / hasBackendFiltersRef kept in sync via a dedicated effect defined before the sort/page/time effect, so React's effect ordering guarantees refs are current when the sort/page/time effect reads them — a correct application of the "ref as always-current value" pattern.

Confidence Score: 5/5

Safe to merge — targeted one-file fix with no new dependencies, correct React refs pattern, and tests passing.

The fix correctly applies the 'ref as always-current value' pattern. The sync effect is defined before the sort/page/time effect, so React's in-order effect execution guarantees refs are up-to-date when consumed. Initial ref values are consistent with initial state, no spurious API calls are introduced on mount, and the existing handleFilterChange → debouncedSearch path is untouched. No P0/P1 findings.

No files require special attention.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/view_logs/log_filter_logic.tsx Adds filtersRef/hasBackendFiltersRef with a sync effect (defined before the sort/page/time effect) to eliminate stale-closure capture; logic is correct and effect ordering guarantees refs are fresh when consumed.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as RequestLogs UI
    participant Hook as useLogFilterLogic
    participant Refs as filtersRef / hasBackendFiltersRef
    participant API as uiSpendLogsCall

    User->>UI: Set Key Alias filter
    UI->>Hook: handleFilterChange(newFilters)
    Hook->>Hook: setFilters(updatedFilters)
    Note over Hook: Re-render → hasBackendFilters = true
    Hook->>Refs: sync effect updates filtersRef.current and hasBackendFiltersRef.current
    Hook->>Hook: debouncedSearch(filters, page=1)
    Hook->>API: performSearch(filters, 1) — correct filters ✓

    User->>UI: Change sort column / page / time range
    UI->>Hook: sortBy/currentPage/startTime prop changes
    Note over Hook: Re-render → sort/page/time effect fires
    Hook->>Refs: Read filtersRef.current (latest) and hasBackendFiltersRef.current (true)
    Hook->>Hook: debouncedSearch.cancel()
    Hook->>API: performSearch(filtersRef.current, currentPage) — filters intact ✓
Loading

Reviews (1): Last reviewed commit: "fix(ui): stale filters applied after sor..." | Re-trigger Greptile

@X4tar

X4tar commented Apr 15, 2026

Copy link
Copy Markdown
Contributor

LGTM

@krrish-berri-2
krrish-berri-2 changed the base branch from litellm_oss_branch to litellm_oss_staging_04_22_2026 April 23, 2026 02:41
@krrish-berri-2
krrish-berri-2 merged commit c26e304 into BerriAI:litellm_oss_staging_04_22_2026 Apr 23, 2026
6 checks passed
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
… Logs (BerriAI#25789)

The useEffect that re-fetches logs on sort/page/time changes:

  useEffect(() => {
    if (hasBackendFilters && accessToken) {
      performSearch(filters, currentPage);
    }
  }, [sortBy, sortOrder, currentPage, startTime, endTime, isCustomDate]);

intentionally omits `filters` and `hasBackendFilters` from its dep array
to avoid double-fetches when a filter is applied.  The side-effect is a
stale-closure bug: the effect captures `filters` and `hasBackendFilters`
from the render where its deps last changed, not from the render where
the user selected, e.g., a Key Alias.

Reproduce: set Key Alias → results appear correctly → change page or
sort → the effect fires with the OLD `filters` snapshot (no key_alias)
→ API request is sent without the filter → table shows unfiltered data.

Fix: store the latest `filters` and `hasBackendFilters` in refs that are
kept in sync on every render.  The sort/page/time effect reads from the
refs instead of the closure so it always uses the current filter state
without altering the dep array.

Co-authored-by: Bytechoreographer <Bytechoreographer@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants