From 94eea41a75fa077fa21a924d84c8cf3e0df8568d Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Mon, 21 Sep 2026 16:44:06 +0200 Subject: [PATCH] fix: surface searchapi errors, news results and redirect links (#30308) Web search via searchapi.io could come back empty or near-empty with no hint of why: an invalid or expired API key turned into an empty result set instead of an error, the google_news engine splits its results between organic_results and top_stories and only the first block was read, and google links came back as google.com/goto redirects the web loader cannot fetch, so citations pointed at a redirect blob. The search now reads both result blocks, asks google engines for resolved destination links, raises on HTTP errors, carries a 30s request timeout, skips result rows without a link, and logs the response body at debug instead of dumping every search at info. Fixes #30305 --- backend/open_webui/retrieval/web/searchapi.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/backend/open_webui/retrieval/web/searchapi.py b/backend/open_webui/retrieval/web/searchapi.py index bd4c292c18..0e9f4b02fe 100644 --- a/backend/open_webui/retrieval/web/searchapi.py +++ b/backend/open_webui/retrieval/web/searchapi.py @@ -26,19 +26,26 @@ def search_searchapi( engine = engine or 'google' payload = {'engine': engine, 'q': query, 'api_key': api_key} + if engine.startswith('google'): + payload['link'] = 'resolved' url = f'{url}?{urlencode(payload)}' - response = requests.request('GET', url) + response = requests.request('GET', url, timeout=30) + response.raise_for_status() json_response = response.json() - log.info('results from searchapi search: %s', json_response) + log.debug('results from searchapi search: %s', json_response) - results = sorted(json_response.get('organic_results', []), key=lambda x: x.get('position', 0)) + # top_stories entries carry no position, so the merged list keeps API order + results = [ + *json_response.get('organic_results', []), + *json_response.get('top_stories', []), + ] if filter_list: results = get_filtered_results(results, filter_list) return [ SearchResult( - link=result['link'], + link=result.get('link', ''), title=result.get('title'), snippet=result.get('snippet'), )