summaryrefslogtreecommitdiff
path: root/searx
diff options
context:
space:
mode:
authorPatrick Evans <holysoles97@gmail.com>2026-06-12 16:25:13 -0500
committerBnyro <bnyro@tutanota.com>2026-07-07 10:49:54 +0200
commitf9304437267b1113086e3cbec47dfb37e15f1112 (patch)
treebc6078968a0193f6c2d8682c33a35aed672b41ab /searx
parentd58ced8f7126ff4a349c4f454af0c47a4e649f06 (diff)
[fix] kagi: safely access unpromised result properties
I was testing the new Kagi engine and found for some queries I was getting a `KeyError` exception from the result parsing. This PR ensures we only assumes the `url` key exists, and we use `.get()` to retrieve values for keys that may not be present. Their API documentation [1] clarifies that only the `url` and `title` properties are required/guaranteed in the search result object, but this is not correct: File "/home/patrick/code/searxng/searx/engines/kagi.py", line 161, in response title=html.unescape(result["title"]), ~~~~~~^^^^^^^^^ KeyError: 'title Heard back from Kagi support: > The image results "title' should be marked as optional, as many images likely > don't have titles - as you've noticed. The only required field there should be > the "url" field. [1] https://kagi.redocly.app/api/docs/openapi/search/search#search/search/t=response&c=200&path=data/search [2] https://github.com/searxng/searxng/issues/2247#issuecomment-4692976877
Diffstat (limited to 'searx')
-rw-r--r--searx/engines/kagi.py25
1 files changed, 15 insertions, 10 deletions
diff --git a/searx/engines/kagi.py b/searx/engines/kagi.py
index b0c394ea6..63ec64c17 100644
--- a/searx/engines/kagi.py
+++ b/searx/engines/kagi.py
@@ -42,11 +42,11 @@ To enable Kagi, add the following to the ``engines`` seciton of
.. _Api Portal: https://help.kagi.com/kagi/api/overview.html
"""
-
from datetime import datetime, timedelta
import typing as t
+
from searx.extended_types import SXNG_Response
from searx.result_types import EngineResults
from searx.utils import html_to_text, parse_duration_string
@@ -76,7 +76,12 @@ kagi_categ: t.Literal["search", "images", "news", "videos"] = "search"
base_url = "https://kagi.com"
safe_search_map = {0: False, 1: True, 2: True}
-time_range_to_days_map: dict[TimeRangeType, int] = {"day": 1, "week": 7, "month": 30, "year": 365}
+time_range_to_days_map: dict[TimeRangeType, int] = {
+ "day": 1,
+ "week": 7,
+ "month": 30,
+ "year": 365,
+}
api_key = ""
"""Kagi API key. Required for using this engine."""
@@ -147,8 +152,8 @@ def response(resp: "SXNG_Response") -> EngineResults:
res.add(
res.types.MainResult(
url=result["url"],
- title=html_to_text(result["title"]),
- content=html_to_text(result["snippet"]),
+ title=html_to_text(result.get("title", "no title available")),
+ content=html_to_text(result.get("snippet", "")),
thumbnail=result.get("image", {}).get("url") or "",
publishedDate=published_date,
)
@@ -157,15 +162,15 @@ def response(resp: "SXNG_Response") -> EngineResults:
res.add(
res.types.Image(
url=result["url"],
- title=html_to_text(result.get("title")),
+ title=html_to_text(result.get("title", "no title available")),
img_src=result.get("image", {}).get("url"),
- resolution=f"{result['image']['width']}x{result['image']['height']}",
+ resolution=f"{result.get('image', {}).get('width')}x{result.get('image', {}).get('height')}",
thumbnail_src=result.get("props", {}).get("thumbnail", {}).get("url"),
)
)
elif kagi_categ == "videos":
length: timedelta | None = None
- if result["props"].get("duration"):
+ if result.get("props", {}).get("duration"):
length = parse_duration_string(result["props"]["duration"])
res.add(
@@ -173,11 +178,11 @@ def response(resp: "SXNG_Response") -> EngineResults:
{
"template": "videos.html",
"url": result["url"],
- "title": html_to_text(result["title"]),
- "content": html_to_text(result["snippet"]),
+ "title": html_to_text(result.get("title", "no title available")),
+ "content": html_to_text(result.get("snippet", "")),
"thumbnail": result.get("image", {}).get("url"),
"publishedDate": published_date,
- "author": result["props"].get("creator_name"),
+ "author": result.get("props", {}).get("creator_name"),
"length": length,
}
)