HKUDS/DeepTutor · error · HubError
{self.name}: unrecognised catalog response shape
Error message
{self.name}: unrecognised catalog response shape What it means
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.
Source
Thrown at deeptutor/services/skill/hub.py:416
summary, version, download/star counts and owner — everything the
DeepTutor-native browser renders without a second round-trip.
"""
if query.strip():
response = self._get("/search", q=query.strip(), limit=limit)
else:
response = self._get("/skills", limit=limit, sort=sort)
try:
payload = response.json()
except ValueError as exc:
raise HubError(f"{self.name}: catalog returned invalid JSON") from exc
rows = payload if isinstance(payload, list) else None
if rows is None and isinstance(payload, dict):
for key in ("results", "skills", "items"):
if isinstance(payload.get(key), list):
rows = payload[key]
break
if rows is None:
raise HubError(f"{self.name}: unrecognised catalog response shape")
out: list[dict[str, Any]] = []
for row in rows:
if isinstance(row, dict):
listing = self._listing_from_row(row)
if listing is not None:
out.append(listing)
return out
def detail(self, slug: str) -> dict[str, Any]:
"""Full metadata + SKILL.md body for one hub skill (browser detail view)."""
try:
payload = self._get(f"/skills/{slug}").json()
except ValueError as exc:
raise HubError(f"{self.name}: skill detail returned invalid JSON") from exc
if not isinstance(payload, dict):
raise HubError(f"{self.name}: unrecognised skill detail shape")
skill = payload.get("skill") if isinstance(payload.get("skill"), dict) else payload
listing = self._listing_from_row(skill) or {"slug": slug, "name": slug}View on GitHub (pinned to 3e82f13042)
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
Example fix
// before: {"payload": {"skills": [...]}}
// after: {"skills": [...]} Defensive patterns
Strategy: validation
Validate before calling
payload = httpx.get(f"{BASE}/skills").json()
rows = 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)
if rows is None: raise SystemExit('unsupported envelope') Type guard
def extract_rows(p: object) -> list | None:
if isinstance(p, list):
return p
if isinstance(p, dict):
for k in ("results", "skills", "items"):
if isinstance(p.get(k), list):
return p[k]
return None Try / catch
try:
hub.catalog()
except HubError as e:
if "unrecognised catalog response shape" in str(e):
return []
raise Prevention
- Contract-test the hub envelope in CI
- Version-lock hub deployments
When it happens
Trigger: 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.
Common situations: Hub API schema drift after an upgrade; hub returning an error envelope with HTTP 200; custom self-hosted hub with a different catalog envelope.
Related errors
- {self.name}: unrecognised search response shape
- {self.name}: catalog returned invalid JSON
- {self.name}: unrecognised skill detail shape
- MinerU API did not return an upload URL (missing batch_id/fi
- MinerU reported done but returned no full_zip_url.
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/10a106918dae6b16.
Report an issue: GitHub.