Skip to content

fix(search): tell a failed search apart from one with no results - #2377

Merged
felladrin merged 2 commits into
felladrin:mainfrom
uuzzrm:fix/search-failure-vs-empty
Aug 15, 2026
Merged

fix(search): tell a failed search apart from one with no results#2377
felladrin merged 2 commits into
felladrin:mainfrom
uuzzrm:fix/search-failure-vs-empty

Conversation

@uuzzrm

@uuzzrm uuzzrm commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Closes #2336.

A SearXNG outage and a query with genuinely zero results used to reach the client as the exact same response, so both ended up with the red "Search failed" alert and its Retry button. Someone searching for something obscure got told the search failed, and a real outage looked like an ordinary empty result.

Server

  • fetchSearXNG no longer swallows upstream failures into an empty array - it logs the failure and rethrows, so an empty array now means exactly one thing: zero results.
  • /search/text and /search/images answer HTTP 502 with a JSON error when SearXNG is unreachable; a zero-result search stays HTTP 200 with [].

Client

  • searchText / searchImages log and rethrow instead of returning [] on error.
  • startTextSearch and startImageSearch mark the state failed only when the request itself errors; a successful empty response leaves the state completed so the existing no-results alert actually renders.
  • The keyword fallback still fires on genuinely empty results, and no longer fires when the search request errors.

Docs & tests

  • docs/failure-injection.md: dropped the Known Limitations entry, updated the matrix rows for the two cases.
  • Updated the rows that pinned the old indistinguishable behavior and added coverage for the 502 endpoint response (text + images) and the client failed-on-error path.
  • Reverse-verified: the updated tests fail against the previous code (16 failures), pass with the fix.

Validation: npx vitest run on the four affected files (90 passed), npx biome check clean on all changed files, npx tsc --noEmit clean. (The repo's two doc-script tests fail on Windows for path-separator reasons unrelated to this change - they fail on main too.)

SearXNG being down and a query with no results used to reach the client as the same empty response, so both showed the red failure alert. fetchSearXNG and the client search service now propagate upstream failures instead of collapsing them into an empty array: /search/text and /search/images answer HTTP 502 on an outage, while a genuine zero-result search stays HTTP 200 with an empty array. The client maps that back to the states it already renders - failed shows the retry alert, completed with zero results shows the no-results alert - and the keyword fallback only fires on genuinely empty results.
A failed search no longer cleared the LLM grounding channel:
updateLlmTextSearchResults was only called on the success path, and the
reset block in searchAndRespond cleared text/image results, chat, and
page contents but not llmTextSearchResults. So after a healthy search,
a subsequent search that failed on an outage left the previous query's
snippets/URLs in the channel, and the AI grounded its answer for the
failed query on stale results.

Clear the channel in the reset block (which runs at the start of every
search, before startTextSearch), so a failed search, an
enableTextSearch:false search, and a getKeywords throw before the try all
leave the channel empty. Add a regression test that pre-populates the
channel and asserts a failed search clears it, and update the
failure-injection matrix and the stale configuration.md line that still
described the pre-fix 'generic failure'.

@felladrin felladrin left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

@uuzzrm Pushed a fix to the branch for the stale-grounding issue. A failed search now clears the LLM channel in the reset block (updateLlmTextSearchResults([])), and I added a regression test that pre-populates the channel and asserts a failed search clears it (it goes red if the fix line is removed).

Two other things I spotted are pre-existing and not from this PR: a superseded search can still write its results into a newer run's channel and history (the results publish has no run-id guard, unlike readPageContents), and a getKeywords throw leaves textSearchState idle instead of failed. I'll track those separately so they don't block this.

Approving.

@felladrin
felladrin merged commit 6941e09 into felladrin:main Aug 15, 2026
3 checks passed
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.

Distinguish a failed search from a search with no results

2 participants