infiniflow/ragflow · error · ValueError

Invalid response from SearXNG

Error message

Invalid response from SearXNG

What it means

The SearXNG agent component GETs {searxng_url}/search with format=json and requires the parsed body to be a non-empty dict (agent/tools/searxng.py:112). A JSON body that is empty, a list, a string, or null raises ValueError('Invalid response from SearXNG'). This usually means the endpoint did not return a SearXNG JSON search payload at all.

Source

Thrown at agent/tools/searxng.py:112

        last_e = ""
        for _ in range(self._param.max_retries + 1):
            if self.check_if_canceled("SearXNG processing"):
                return

            try:
                search_params = {"q": query, "format": "json", "categories": "general", "language": "auto", "safesearch": 1, "pageno": 1}

                with pin_dns(_ssrf_hostname, _ssrf_ip):
                    response = requests.get(f"{searxng_url}/search", params=search_params, timeout=10)
                response.raise_for_status()

                if self.check_if_canceled("SearXNG processing"):
                    return

                data = response.json()

                if not data or not isinstance(data, dict):
                    raise ValueError("Invalid response from SearXNG")

                results = data.get("results", [])
                if not isinstance(results, list):
                    raise ValueError("Invalid results format from SearXNG")

                results = results[: self._param.top_n]

                if self.check_if_canceled("SearXNG processing"):
                    return

                self._retrieve_chunks(results, get_title=lambda r: r.get("title", ""), get_url=lambda r: r.get("url", ""), get_content=lambda r: r.get("content", ""))

                self.set_output("json", results)
                return self.output("formalized_content")

            except requests.RequestException as e:
                if self.check_if_canceled("SearXNG processing"):
                    return

View on GitHub (pinned to 554fb1133a)

Solutions

  1. In SearXNG settings.yml enable the JSON format: search: formats: [html, json], then restart SearXNG.
  2. Verify manually: curl 'http://<host>/search?q=test&format=json' must return a JSON object with a results array.
  3. Check searxng_url has no trailing /search or wrong port - the code appends /search itself.
  4. If a proxy or bot-limiter intercepts the request, whitelist the RAGFlow server IP or adjust limiter settings.

Example fix

# before (searxng settings.yml)
search:
  formats:
    - html

# after
search:
  formats:
    - html
    - json
Defensive patterns

Strategy: try-catch

Validate before calling

import requests

def searxng_ready(base_url):
    try:
        r = requests.get(f"{base_url}/search", params={"q": "test", "format": "json"}, timeout=5)
        return r.ok and isinstance(r.json(), dict)
    except Exception:
        return False

Try / catch

try:
    output = searxng_component.run(query)
except ValueError as e:
    if "Invalid response" in str(e):
        # endpoint healthy check failed: fall back to another search tool or surface config error
        raise ConfigError("SearXNG JSON format disabled or URL wrong - check settings.yml") from e
    raise

Prevention

When it happens

Trigger: searxng_url points at a SearXNG instance where the 'json' output format is disabled (default in modern SearXNG), so /search returns an HTML page or 403-related body; the URL points at a non-SearXNG service; a proxy returns a JSON array or error object; response.json() parses something that is not an object.

Common situations: Self-hosted SearXNG without enabling json in settings.yml search.formats; trailing-path mistakes in searxng_url; reverse proxy rewriting responses; SearXNG limiter returning a non-JSON block page.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/a84b141daac1c8b4. Report an issue: GitHub.