HKUDS/DeepTutor · error · HubError
{self.name}: unrecognised search response shape
Error message
{self.name}: unrecognised search response shape What it means
Raised by HubProvider.search when the hub's /search endpoint returns a payload that is neither a JSON list nor a dict containing a 'results', 'items', or 'skills' list. The client cannot locate any rows to normalise into HubSkillRef objects, so it aborts rather than returning misleading empty results.
Source
Thrown at deeptutor/services/skill/hub.py:301
raise HubError(
f"{self.name}: HTTP {response.status_code} for {path}: {response.text[:200]}"
)
return response
def search(self, query: str, *, limit: int = 10) -> list[HubSkillRef]:
response = self._get("/search", q=query, limit=limit)
try:
payload = response.json()
except ValueError as exc:
raise HubError(f"{self.name}: search 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", "items", "skills"):
if isinstance(payload.get(key), list):
rows = payload[key]
break
if rows is None:
raise HubError(f"{self.name}: unrecognised search response shape")
refs: list[HubSkillRef] = []
for row in rows:
if not isinstance(row, dict):
continue
slug = str(row.get("slug") or "").strip()
if not slug:
continue
refs.append(
HubSkillRef(
hub=self.name,
slug=slug,
display_name=str(row.get("displayName") or row.get("name") or slug),
summary=str(row.get("summary") or row.get("description") or ""),
version=str(row.get("version") or ""),
)
)
return refs
View on GitHub (pinned to 3e82f13042)
Solutions
- Inspect the raw response: curl $BASE_URL/search?q=test and check the envelope key holding the list
- If the list sits under a different key, ensure the hub returns one of results/items/skills at the top level (or a bare array)
- Verify base_url in settings/skill_hubs.json points at a compatible hub API version
- If the hub genuinely cannot conform, wrap it with a CommandProvider instead
Example fix
// settings/skill_hubs.json (hub must return one of these shapes)
// before: {"data": {"results": [...]}}
// after: {"results": [...]} Defensive patterns
Strategy: validation
Validate before calling
import httpx
resp = httpx.get(f"{BASE}/search", params={"q": q})
payload = resp.json()
assert isinstance(payload, list) or any(isinstance(payload.get(k), list) for k in ("results","items","skills") if isinstance(payload, dict)), "envelope not supported" Type guard
def has_supported_rows(p: object) -> bool:
if isinstance(p, list):
return True
return isinstance(p, dict) and any(isinstance(p.get(k), list) for k in ("results", "items", "skills")) Try / catch
try:
refs = hub.search(q)
except HubError as e:
if "unrecognised search response shape" in str(e):
return [] # degrade gracefully
raise Prevention
- Pin the hub API version you develop against
- Add a smoke check of /search envelope at startup in dev/test
When it happens
Trigger: Calling hub.search(query) (or `skill search`) against a ClawHub-like provider whose /search response is a bare dict like {"error": ...}, a scalar, or an envelope using an unexpected key (e.g. {'data': {'hits': [...]}} with double nesting).
Common situations: Hub API version change that altered the response envelope; pointing base_url at a different service or an HTML error page that parsed as a scalar; a proxy returning {"detail": "..."} on auth failure.
Related errors
- {self.name}: unrecognised catalog response shape
- {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.
- MinerU API returned an unexpected (non-JSON) response.
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/8e46180649c88f5f.
Report an issue: GitHub.