infiniflow/ragflow · error · ValueError

Invoke headers must be a JSON object.

Error message

Invoke headers must be a JSON object.

What it means

Thrown while building HTTP headers for the Invoke component. The configured headers string is parsed with json.loads and must deserialize to a JSON object (Python dict); any other top-level JSON type (array, string, number, boolean) triggers this ValueError.

Source

Thrown at agent/component/invoke.py:190

                self.set_input_value(para["ref"], value)
        return args

    def _build_url(self, kwargs: dict) -> str:
        url = self._resolve_template_text(self._param.url.strip(), kwargs)
        if not url.startswith(("http://", "https://")):
            url = "http://" + url
        hostname, ip = assert_url_is_safe(url)
        self._pinned_hostname = hostname
        self._pinned_ip = ip
        return url

    def _build_headers(self, kwargs: dict) -> dict:
        if not self._param.headers:
            return {}

        headers = json.loads(self._param.headers)
        if not isinstance(headers, dict):
            raise ValueError("Invoke headers must be a JSON object.")

        return {key: self._resolve_header_text(value, kwargs) if isinstance(value, str) else value for key, value in headers.items()}

    @staticmethod
    def _ssrf_log_target(url: str) -> str:
        parsed = urlparse(url)
        if not parsed.scheme or not parsed.hostname:
            return "invalid-url"
        return f"{parsed.scheme}://{parsed.hostname}"

    def _normalize_proxy_url(self) -> str | None:
        proxy = (self._param.proxy or "").strip()
        if not re.sub(r"https?:?/?/?", "", proxy):
            return None
        if not proxy.startswith(("http://", "https://")):
            proxy = "http://" + proxy
        return proxy

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Change the headers configuration to a flat JSON object: {"Authorization": "Bearer {{token}}", "Content-Type": "application/json"}.
  2. If you copied [{"name":"...","value":"..."}] pairs, flatten them into a single object with name as key and value as value.
  3. Validate the headers string with json.loads in a scratch step before saving the component.

Example fix

# before (list of pairs)
"headers": "[{\"name\": \"X-Api-Key\", \"value\": \"123\"}]"

# after (JSON object)
"headers": "{\"X-Api-Key\": \"123\"}"
Defensive patterns

Strategy: validation

Validate before calling

import json

def valid_headers(cfg: str) -> bool:
    try:
        return isinstance(json.loads(cfg), dict) if cfg else True
    except json.JSONDecodeError:
        return False

Type guard

import json

def headers_is_object(cfg: str) -> bool:
    if not cfg:
        return True
    try:
        return isinstance(json.loads(cfg), dict)
    except json.JSONDecodeError:
        return False

Try / catch

try:
    headers = json.loads(param_headers)
except json.JSONDecodeError:
    raise ValueError('Invoke headers is not valid JSON') from None
if not isinstance(headers, dict):
    raise ValueError('Invoke headers must be a JSON object.')

Prevention

When it happens

Trigger: Setting the Invoke component's headers parameter to a JSON array like '["Authorization: Bearer x"]' or a bare string like '"Authorization: Bearer x"' instead of an object. Note that malformed JSON raises json.JSONDecodeError earlier; this error is specifically valid-JSON-but-wrong-shape.

Common situations: Copying cURL-style header lines into the headers field; pasting a header list produced by browser devtools 'copy as JSON'; templates that emit a list of {name, value} pairs.

Related errors


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