File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff 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-
Original file line number Diff line number Diff 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 = '' ;
Original file line number Diff line number Diff 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
123123class TestSearchEdgeCases :
You can’t perform that action at this time.
0 commit comments