MemPalace/mempalace · error · LLMError

Malformed response from {url}: {e}

Error message

Malformed response from {url}: {e}

What it means

LLMError raised when the LLM endpoint returns HTTP 200 but the body is not valid JSON (json.JSONDecodeError). The provider pipeline assumes every successful response parses, so a non-JSON body — an HTML error page, a proxy login page, truncated output — surfaces here.

Source

Thrown at mempalace/llm_client.py:199

    req = Request(
        url,
        data=json.dumps(body).encode("utf-8"),
        headers={"Content-Type": "application/json", **headers},
    )
    try:
        with urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read())
    except HTTPError as e:
        detail = ""
        try:
            detail = e.read().decode("utf-8", errors="replace")[:500]
        except Exception:
            pass
        raise LLMError(f"HTTP {e.code} from {url}: {detail or e.reason}") from e
    except (URLError, OSError) as e:
        raise LLMError(f"Cannot reach {url}: {e}") from e
    except json.JSONDecodeError as e:
        raise LLMError(f"Malformed response from {url}: {e}") from e


# ==================== OLLAMA ====================


class OllamaProvider(LLMProvider):
    name = "ollama"
    DEFAULT_ENDPOINT = "http://localhost:11434"

    def __init__(
        self,
        model: str,
        endpoint: Optional[str] = None,
        timeout: int = 180,
        num_ctx: Optional[int] = None,
        **_: object,
    ):
        super().__init__(

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. curl the endpoint with the same payload and inspect the raw body — it is usually HTML/plain text, revealing the real culprit
  2. Fix the endpoint URL to target the API server, not a web UI or proxy login page
  3. Bypass or configure the intercepting proxy for this host
  4. If the body is truncated JSON, check gateway/proxy response size limits
Defensive patterns

Strategy: retry

Try / catch

from mempalace.llm_client import LLMError

try:
    resp = provider.classify(system, user)
except LLMError as e:
    if "Malformed response" in str(e):
        # body was not JSON — dump context for debugging, do not blind-retry
        log.error("non-JSON response from %s — check proxy/gateway", provider.endpoint)
        raise

Prevention

When it happens

Trigger: A proxy or captive portal intercepting the request and returning HTML; an OpenAI-compatible server that returns plain-text errors with status 200; a response truncated by a gateway so the JSON is cut mid-string.

Common situations: Corporate proxies rewriting responses; pointing --llm-endpoint at a web UI instead of the API port (e.g. LM Studio UI port vs API port); reverse proxies (nginx/traefik) emitting error pages; version mismatch where the server changed its response format.

Understand the failure class

Related errors


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/5967e5c220961843. Report an issue: GitHub.