diff --git a/frontend/src/lib/stores/search.svelte.ts b/frontend/src/lib/stores/search.svelte.ts index fd4dffec..7a469302 100644 --- a/frontend/src/lib/stores/search.svelte.ts +++ b/frontend/src/lib/stores/search.svelte.ts @@ -56,7 +56,7 @@ class SearchStore { { project: project || undefined, limit: 30 }, { signal }, ); - this.results = res.results; + this.results = res.results ?? []; } catch (error: unknown) { if (error instanceof DOMException && error.name === "AbortError") { diff --git a/internal/server/search.go b/internal/server/search.go index abd6e23a..891f0a8c 100644 --- a/internal/server/search.go +++ b/internal/server/search.go @@ -68,10 +68,14 @@ func (s *Server) handleSearch( return } + results := page.Results + if results == nil { + results = []db.SearchResult{} + } writeJSON(w, http.StatusOK, searchResponse{ Query: query, - Results: page.Results, - Count: len(page.Results), + Results: results, + Count: len(results), Next: page.NextCursor, }) } diff --git a/internal/server/server_test.go b/internal/server/server_test.go index bd052714..7b11fe9b 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -894,6 +894,26 @@ func TestSearch_DeadlineExceeded(t *testing.T) { assertTimeoutRace(t, w) } +func TestSearch_ZeroResults(t *testing.T) { + te := setup(t) + if !te.db.HasFTS() { + t.Skip("skipping search test: no FTS support") + } + te.seedSession(t, "s1", "my-app", 1) + te.seedMessages(t, "s1", 1) + + w := te.get(t, "/api/v1/search?q=spamalot") + assertStatus(t, w, http.StatusOK) + + resp := decode[searchResponse](t, w) + if resp.Results == nil { + t.Fatal("results must be [] not null") + } + if resp.Count != 0 { + t.Fatalf("expected count=0, got %d", resp.Count) + } +} + func TestSearch_NotAvailable(t *testing.T) { te := setup(t) // Simulate missing FTS by dropping the virtual table.