Skip to content

fix: search empty state never renders due to stale fetch race condition (#144)#146

Merged
zacharias-ona merged 1 commit into
mainfrom
fix/144-search-empty-state
Apr 17, 2026
Merged

fix: search empty state never renders due to stale fetch race condition (#144)#146
zacharias-ona merged 1 commit into
mainfrom
fix/144-search-empty-state

Conversation

@zacharias-ona
Copy link
Copy Markdown
Collaborator

Closes #144

What

The search empty state ("No pages match your search") never renders when searching for a nonsense string. Instead, skeleton loading placeholders show indefinitely. This bug persisted across multiple fix attempts (#126, #136) because the root cause — stale fetch responses corrupting component state — was never addressed.

How

Added AbortController to cancel in-flight fetch requests when the search query or workspace changes. Without cancellation, a stale fetch response could set loading = false at the wrong time (e.g., between a new debounce cycle setting loading = true and the new fetch completing), leaving the component in an inconsistent state where neither skeletons nor the empty state rendered.

Key changes:

  • Each debounce cycle creates an AbortController and passes its signal to fetch()
  • The effect cleanup and re-entry both abort the previous controller
  • All state updates after await are guarded with signal.aborted checks
  • The debounce effect resets searched = false when starting a new search cycle, preventing stale searched = true from a previous cycle

Testing

  • Added regression test "aborts stale fetch when query changes rapidly" that verifies abort signals are sent and stale responses don't corrupt state
  • All 141 unit tests pass (pnpm test)
  • All 42 E2E tests pass (pnpm test:e2e), including the previously failing search with no matches shows empty state
  • pnpm lint && pnpm typecheck pass

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 17, 2026

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

Project Deployment Actions Updated (UTC)
memo Ready Ready Preview, Comment Apr 17, 2026 0:26am

Request Review

@zacharias-ona zacharias-ona merged commit b88be5a into main Apr 17, 2026
6 checks passed
@zacharias-ona zacharias-ona deleted the fix/144-search-empty-state branch April 17, 2026 00:29
@zacharias-ona
Copy link
Copy Markdown
Collaborator Author

✅ UI verification passed — design spec compliance confirmed.

Static analysis: All changes are behavioral (AbortController for fetch cancellation, signal.aborted guards). No color tokens, typography, spacing, component usage, or accessibility attributes were modified. No design spec violations.

Visual verification (Playwright screenshots on production):

  • Desktop dark: sidebar layout, search input, page tree all render correctly
  • Search empty state: "No pages match your search" renders with text-xs text-muted-foreground — appropriate for inline dropdown context
  • Mobile: sidebar collapsed to hamburger, content full-width, no horizontal scroll

The fix correctly resolves the stale fetch race condition — the empty state now appears instead of infinite skeletons.

@zacharias-ona
Copy link
Copy Markdown
Collaborator Author

✅ Post-merge verification passed.

E2E suite: 39/42 passed. 3 pre-existing flaky failures in editor-drag.spec.ts (drag handle visibility timeout) — unrelated to this PR's search changes.

Ad-hoc smoke tests — all passed:

  • ✅ Landing page (/) — loaded, has title
  • ✅ Sign-in page (/sign-in) — rendered with email input
  • ✅ Health endpoint (/api/health) — healthy
  • ✅ Authenticated login flow — redirected to workspace
  • ✅ Workspace page — fully loaded
  • ✅ Editor navigation — sidebar page button navigated to editor route, editor rendered
  • ✅ No console errors (unauthenticated or authenticated)

Skipped: /dashboard (not yet built)

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.

bug: search empty state still broken — "No pages match your search" never renders

1 participant