{"record":{"id":"10a106918dae6b16","repo":"HKUDS/DeepTutor","slug":"self-name-unrecognised-catalog-response-shape","errorCode":null,"errorMessage":"{self.name}: unrecognised catalog response shape","messagePattern":"(.+?): unrecognised catalog response shape","errorType":"http","errorClass":"HubError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/skill/hub.py","lineNumber":416,"sourceCode":"        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).\"\"\"\n        try:\n            payload = self._get(f\"/skills/{slug}\").json()\n        except ValueError as exc:\n            raise HubError(f\"{self.name}: skill detail returned invalid JSON\") from exc\n        if not isinstance(payload, dict):\n            raise HubError(f\"{self.name}: unrecognised skill detail shape\")\n        skill = payload.get(\"skill\") if isinstance(payload.get(\"skill\"), dict) else payload\n        listing = self._listing_from_row(skill) or {\"slug\": slug, \"name\": slug}","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/skill/hub.py#L398-L434","documentation":"Raised by HubProvider.catalog when the parsed JSON payload is neither a top-level list nor a dict containing a 'results', 'skills', or 'items' list. The client refuses to guess rows and aborts instead of silently returning an empty catalog.","triggerScenarios":"Calling catalog() when the hub responds with a dict like {\"error\": \"...\"}, a scalar/null payload, or a nested envelope whose list lives deeper than one level.","commonSituations":"Hub API schema drift after an upgrade; hub returning an error envelope with HTTP 200; custom self-hosted hub with a different catalog envelope.","solutions":["curl the endpoint and confirm the list lives under results/skills/items or at the top level","Update the hub to emit one of the expected keys","Pin or align hub API version with the client's expected envelope","Fall back to CommandProvider for non-conforming hubs"],"exampleFix":"// before: {\"payload\": {\"skills\": [...]}}\n// after:  {\"skills\": [...]}","handlingStrategy":"validation","validationCode":"payload = httpx.get(f\"{BASE}/skills\").json()\nrows = payload if isinstance(payload, list) else next((payload.get(k) for k in (\"results\",\"skills\",\"items\") if isinstance(payload, dict) and isinstance(payload.get(k), list)), None)\nif rows is None: raise SystemExit('unsupported envelope')","typeGuard":"def extract_rows(p: object) -> list | None:\n    if isinstance(p, list):\n        return p\n    if isinstance(p, dict):\n        for k in (\"results\", \"skills\", \"items\"):\n            if isinstance(p.get(k), list):\n                return p[k]\n    return None","tryCatchPattern":"try:\n    hub.catalog()\nexcept HubError as e:\n    if \"unrecognised catalog response shape\" in str(e):\n        return []\n    raise","preventionTips":["Contract-test the hub envelope in CI","Version-lock hub deployments"],"tags":["hub","catalog","response-shape","api-contract"],"backgroundTag":"unexpected-response-schema","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}