Skip to content

Commit 463e35d

Browse files
fix(#117): address PR #126 review feedback on search UX and errors
1 parent fbd9ba1 commit 463e35d

3 files changed

Lines changed: 20 additions & 8 deletions

File tree

api/search.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,13 @@ def search() -> tuple[Response, int] | Response:
105105
Args:
106106
q: Search query string (required; 400 when empty).
107107
type: Filter scope — ``all`` (default), ``chat``, or ``composer``.
108-
workspace: Optional workspace folder hash; 404 when unknown.
108+
workspace: Optional workspace folder hash; 404 when unknown (bonus API
109+
filter — not exposed in the search UI).
109110
110111
Returns:
111112
JSON ``{"results": [...]}`` with optional ``warnings``. Structured
112-
``{"error", "code"}`` bodies for 400/404/503/500 failures.
113+
``{"error", "code"}`` bodies for 400/404/503/500 failures. Error
114+
responses omit ``results`` (breaking change vs. legacy 500 bodies).
113115
"""
114116
query = request.args.get("q", "").strip()
115117
if not query:
@@ -203,7 +205,13 @@ def search() -> tuple[Response, int] | Response:
203205
"search_index_unavailable",
204206
503,
205207
)
208+
except OSError:
209+
_logger.exception("Workspace storage unavailable")
210+
return _search_error(
211+
"Workspace storage is temporarily unavailable",
212+
"storage_unavailable",
213+
503,
214+
)
206215
except Exception:
207216
_logger.exception("Search failed")
208217
return _search_error("Search failed", "internal_error", 500)
209-

templates/search.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ <h1>Search</h1>
1818
type="button"
1919
class="search-info-tooltip"
2020
id="search-window-help"
21+
aria-label="Search window help"
2122
aria-describedby="search-window-help-text"
2223
>
2324
<span class="search-info-icon" aria-hidden="true">i</span>
@@ -112,7 +113,10 @@ <h1>Search</h1>
112113
showIncompleteResultsBanner('parse-warnings-host', []);
113114
loading.style.display = 'none';
114115
const message = typeof data.error === 'string' ? data.error : 'Search failed.';
115-
container.innerHTML = `<p class="text-danger">${escapeHtml(message)}</p>`;
116+
const codeAttr = typeof data.code === 'string'
117+
? ` data-error-code="${escapeHtml(data.code)}"`
118+
: '';
119+
container.innerHTML = `<p class="text-danger"${codeAttr}>${escapeHtml(message)}</p>`;
116120
return;
117121
}
118122
const results = data.results || [];
@@ -122,7 +126,7 @@ <h1>Search</h1>
122126
countEl.style.display = 'block';
123127
const windowNote = data.allHistory
124128
? ' (all history)'
125-
: ` (last ${data.searchWindowDays || 30} days)`;
129+
: ` (last ${data.searchWindowDays || 30} days; undated chats included)`;
126130
countEl.textContent = `Found ${results.length} results for "${query}"${windowNote}`;
127131

128132
let html = '';

tests/test_api_search.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ def test_index_lock_returns_503(self, client):
110110
_assert_error_body(body, code="search_index_unavailable")
111111
assert "results" not in body
112112

113-
def test_workspace_path_resolution_failure_returns_structured_500(self, client):
113+
def test_workspace_path_resolution_failure_returns_structured_503(self, client):
114114
with patch(
115115
"api.search.resolve_workspace_path",
116116
side_effect=OSError("simulated storage discovery failure"),
117117
):
118118
response = client.get("/api/search?q=sentinel-grep&all_history=1")
119-
assert response.status_code == 500
120-
_assert_error_body(response.get_json(), code="internal_error")
119+
assert response.status_code == 503
120+
_assert_error_body(response.get_json(), code="storage_unavailable")
121121

122122

123123
class TestSearchEdgeCases:

0 commit comments

Comments
 (0)