{"record":{"id":"7c082cdb1834a150","repo":"HKUDS/DeepTutor","slug":"self-name-catalog-returned-invalid-json","errorCode":null,"errorMessage":"{self.name}: catalog returned invalid JSON","messagePattern":"(.+?): catalog returned invalid JSON","errorType":"http","errorClass":"HubError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/skill/hub.py","lineNumber":408,"sourceCode":"\n    def catalog(\n        self, *, query: str = \"\", limit: int = 50, sort: str = \"createdAt\"\n    ) -> list[dict[str, Any]]:\n        \"\"\"List skills published on the hub, for the in-app skill browser.\n\n        Uses ``/search`` when a query is given (server-side relevance) and the\n        plain ``/skills`` explore feed otherwise. Rows carry display name,\n        summary, version, download/star counts and owner — everything the\n        DeepTutor-native browser renders without a second round-trip.\n        \"\"\"\n        if query.strip():\n            response = self._get(\"/search\", q=query.strip(), limit=limit)\n        else:\n            response = self._get(\"/skills\", limit=limit, sort=sort)\n        try:\n            payload = response.json()\n        except ValueError as exc:\n            raise HubError(f\"{self.name}: catalog returned invalid JSON\") from exc\n        rows = payload if isinstance(payload, list) else None\n        if rows is None and isinstance(payload, dict):\n            for key in (\"results\", \"skills\", \"items\"):\n                if isinstance(payload.get(key), list):\n                    rows = payload[key]\n                    break\n        if rows is None:\n            raise HubError(f\"{self.name}: unrecognised catalog response shape\")\n        out: list[dict[str, Any]] = []\n        for row in rows:\n            if isinstance(row, dict):\n                listing = self._listing_from_row(row)\n                if listing is not None:\n                    out.append(listing)\n        return out\n\n    def detail(self, slug: str) -> dict[str, Any]:\n        \"\"\"Full metadata + SKILL.md body for one hub skill (browser detail view).\"\"\"","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/skill/hub.py#L390-L426","documentation":"Raised by HubProvider.catalog when the HTTP response body cannot be parsed as JSON (response.json() raises ValueError). The catalog endpoint returned HTML, plain text, or an empty body — typically a gateway error page or wrong base_url.","triggerScenarios":"Calling catalog() (used by hub_catalog and the tests test_clawhub_catalog_normalises_rows / test_clawhub_catalog_uses_search_when_queried) when /skills or /search responds with non-JSON such as a 502 HTML page, an empty body, or a text/plain message.","commonSituations":"Reverse proxy or CDN serving an error page; base_url misconfigured to a non-API host; hub behind auth wall returning an HTML login redirect; trailing-slash or path-join bug producing a 404 HTML page.","solutions":["Check response status and Content-Type: curl -i $BASE_URL/skills","Fix base_url in settings/skill_hubs.json to the actual API root","If a proxy intercepts the call, add an exception or correct headers","Retry transient gateway failures (502/503) before giving up"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"resp = httpx.get(f\"{BASE}/skills\")\nif \"application/json\" not in resp.headers.get(\"content-type\", \"\"):\n    raise RuntimeError(f\"non-JSON content-type: {resp.status_code}\")","typeGuard":null,"tryCatchPattern":"try:\n    listings = hub.catalog()\nexcept HubError as e:\n    if \"invalid JSON\" in str(e):\n        log.warning(\"hub returned non-JSON; possibly down: %s\", e)\n        return []\n    raise","preventionTips":["Monitor hub Content-Type in integration tests","Retry on 5xx before surfacing to users"],"tags":["hub","catalog","invalid-json","network"],"backgroundTag":"invalid-json-response","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}