ruvnet/RuView · error · ValueError

Non-JSON response from {method} {path} (status {resp.status}

Error message

Non-JSON response from {method} {path} (status {resp.status}): {raw[:200]!r}

What it means

SeedClient._request() in scripts/seed_csi_bridge.py issues JSON-over-HTTPS calls (urllib, Bearer token, TLS verification disabled for the bridge's self-signed cert) to the Cognitum Seed REST API. When the server responds with a 2xx status but the body fails json.loads, it raises ValueError including the method, path, status, and the first 200 raw bytes. A non-JSON body almost always means the URL reached something other than the Seed API.

Source

Thrown at scripts/seed_csi_bridge.py:240

                 timeout: int = 10, auth: bool = True) -> dict:
        """Issue an HTTP request and return parsed JSON.

        Raises urllib.error.URLError on connection failure,
        urllib.error.HTTPError on non-2xx status, and
        ValueError on non-JSON response body.
        """
        url = f"{self.base_url}{path}"
        data = json.dumps(body).encode() if body is not None else None
        headers = {"Content-Type": "application/json"}
        if auth:
            headers["Authorization"] = f"Bearer {self.token}"
        req = urllib.request.Request(url, data=data, headers=headers, method=method)
        with urllib.request.urlopen(req, context=self.ctx, timeout=timeout) as resp:
            raw = resp.read()
            try:
                return json.loads(raw)
            except (json.JSONDecodeError, ValueError) as exc:
                raise ValueError(
                    f"Non-JSON response from {method} {path} "
                    f"(status {resp.status}): {raw[:200]!r}"
                ) from exc

    def ingest(self, vectors: list[tuple[int, list[float]]]) -> dict:
        """Ingest vectors into the RVF store."""
        return self._request("POST", "/api/v1/store/ingest", {"vectors": vectors})

    def query(self, vector: list[float], k: int = 5) -> dict:
        """Query kNN for a vector."""
        return self._request("POST", "/api/v1/store/query", {"vector": vector, "k": k})

    def compact(self) -> dict:
        """Trigger store compaction."""
        return self._request("POST", "/api/v1/store/compact")

    def status(self) -> dict:
        """Get device status."""

View on GitHub (pinned to 4685618388)

Solutions

  1. Reproduce with curl and inspect the body: `curl -k -H 'Authorization: Bearer <token>' -d '{"k":1}' <base_url>/api/v1/store/query` — the first 200 bytes in the error already tell you what answered
  2. Fix base_url to the exact host:port of the Seed bridge (no trailing slash needed; the client rstrips it)
  3. If a reverse proxy fronts the bridge, add routing so /api/v1/* reaches it and errors stay JSON
  4. Confirm the deployment actually serves the /api/v1/store/* routes at that URL

Example fix

# before
client = SeedClient("https://10.0.0.1", token)  # 10.0.0.1 is the router admin UI -> HTML 200

# after
client = SeedClient("https://10.0.0.1:8443", token)  # actual Seed bridge port
Defensive patterns

Strategy: try-catch

Validate before calling

# Probe that the endpoint speaks JSON before bulk ingesting:
resp = client._request("POST", "/api/v1/store/query", {"vector": [0.0] * 128, "k": 1})  # one cheap call first

Type guard

def is_seed_api_response(payload: bytes) -> bool:
    import json
    try:
        json.loads(payload)
        return True
    except (json.JSONDecodeError, ValueError):
        return False

Try / catch

try:
    result = client.ingest(vectors)
except ValueError as e:
    # e message embeds status + first 200 raw bytes: read it to identify HTML proxy pages
    raise SystemExit(f"Seed bridge not speaking JSON — check base_url/port: {e}") from e
except urllib.error.URLError as e:
    raise SystemExit(f"Seed bridge unreachable: {e}") from e

Prevention

When it happens

Trigger: POST /api/v1/store/ingest or /api/v1/store/query where base_url points at a wrong host/port serving HTML (router admin page, reverse-proxy error page, captive portal), the server returns an empty 200 body, or a redirect lands on a login page that answers 200 with HTML.

Common situations: Wrong port in the base_url; an nginx/ingress in front that returns a styled 200/3xx page for unknown routes; the API deployed at a subpath while the client assumes root; token flow changed so the server returns a plain-text response instead of JSON.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/a09a6726050cdb0e. Report an issue: GitHub.