{"record":{"id":"bd1a6480f685c3d0","repo":"HKUDS/DeepTutor","slug":"self-name-unrecognised-skill-detail-shape","errorCode":null,"errorMessage":"{self.name}: unrecognised skill detail shape","messagePattern":"(.+?): unrecognised skill detail shape","errorType":"http","errorClass":"HubError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/skill/hub.py","lineNumber":432,"sourceCode":"                    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}\n        # Owner sometimes lives at the envelope top level, not inside ``skill``.\n        if not listing.get(\"owner\") and isinstance(payload.get(\"owner\"), dict):\n            owner = payload[\"owner\"]\n            listing[\"owner\"] = str(owner.get(\"displayName\") or owner.get(\"handle\") or \"\")\n            listing[\"owner_url\"] = str(owner.get(\"htmlUrl\") or \"\")\n        dist = payload.get(\"distTags\") if isinstance(payload.get(\"distTags\"), dict) else {}\n        listing[\"version\"] = str(dist.get(\"latest\") or listing.get(\"version\") or \"\")\n        listing[\"content\"] = str(skill.get(\"description\") or \"\")\n        # EduHub's ``tags`` field is a dist-tags map; the topical labels live in\n        # ``keywords``. Fall back to a list-shaped ``tags`` for other hubs.\n        keywords = skill.get(\"keywords\")\n        if not isinstance(keywords, list):\n            keywords = skill.get(\"tags\") if isinstance(skill.get(\"tags\"), list) else []\n        listing[\"tags\"] = [str(t) for t in keywords if str(t).strip()]\n        return listing\n","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/skill/hub.py#L414-L450","documentation":"Raised by HubProvider.detail when the parsed JSON for /skills/{slug} is not a dict (e.g. a list or a string). The detail view needs an object with at least a nested 'skill' object or flat skill fields.","triggerScenarios":"Calling detail(slug) when the hub returns a JSON array (e.g. a list of matches) or a scalar for a slug endpoint.","commonSituations":"Hub version that returns list-shaped search results for unknown slugs instead of an object; misrouted endpoint returning collection payloads.","solutions":["Confirm the endpoint returns an object: {\"skill\": {...}, \"body\": \"...\", ...}","Search for the correct slug via hub.search() before calling detail","Align hub API version with the client contract"],"exampleFix":"// before: [{\"slug\": \"x\", ...}]\n// after:  {\"skill\": {\"slug\": \"x\", ...}, \"body\": \"# ...\"}","handlingStrategy":"type-guard","validationCode":"p = httpx.get(f\"{BASE}/skills/{slug}\").json()\nif not isinstance(p, dict): skip_detail(slug)","typeGuard":"def is_detail_dict(p: object) -> bool:\n    return isinstance(p, dict)","tryCatchPattern":"try:\n    hub.detail(slug)\nexcept HubError as e:\n    if \"unrecognised skill detail shape\" in str(e):\n        return {\"slug\": slug, \"name\": slug}  # minimal fallback\n    raise","preventionTips":["Confirm slugs exist via search() before fetching detail"],"tags":["hub","detail","response-shape"],"backgroundTag":"unexpected-response-schema","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}