{"record":{"id":"f7090bb8e2edfd23","repo":"HKUDS/DeepTutor","slug":"capability-not-found-resolved","errorCode":null,"errorMessage":"Capability not found: {resolved}","messagePattern":"Capability not found: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/app/facade.py","lineNumber":96,"sourceCode":"        contracts = []\n        for manifest in self.capabilities.get_manifests():\n            contracts.append(\n                {\n                    **manifest,\n                    \"availability\": self.get_capability_availability(manifest[\"name\"]).__dict__,\n                }\n            )\n        return contracts\n\n    def get_capability_contract(self, value: str) -> dict[str, Any]:\n        resolved = self.resolve_capability(value)\n        for manifest in self.capabilities.get_manifests():\n            if manifest[\"name\"] == resolved:\n                return {\n                    **manifest,\n                    \"availability\": self.get_capability_availability(resolved).__dict__,\n                }\n        raise ValueError(f\"Capability not found: {resolved}\")\n\n    def get_capability_availability(self, capability: str) -> CapabilityAvailability:\n        resolved = self.resolve_capability(capability)\n        if resolved == \"math_animator\":\n            available = importlib.util.find_spec(\"manim\") is not None\n            return CapabilityAvailability(\n                name=resolved,\n                available=available,\n                install_hint=(\n                    \"\"\n                    if available\n                    else \"Install with `pip install -e '.[math-animator]'` \"\n                    \"or `pip install -r requirements/math-animator.txt`.\"\n                ),\n            )\n        return CapabilityAvailability(name=resolved, available=True)\n\n    async def start_turn(","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/app/facade.py#L78-L114","documentation":"A defensive invariant in `get_capability_contract`: after `resolve_capability` returned a canonical name, no manifest with that `name` was found in the registry. Normally unreachable (resolve checks the same manifests), so hitting it means the registry mutated between the two calls or manifests are inconsistent.","triggerScenarios":"Calling `app.get_capability_contract(name)` while another thread/task unregisters or reloads capabilities between `resolve_capability` and the manifest loop; or a registry implementation whose `get_manifests()` returns a filtered/different set than the one used for resolution.","commonSituations":"Concurrent capability hot-reload during a WebSocket turn; a custom registry subclass that filters manifests inconsistently; monkeypatched manifests in tests.","solutions":["Retry the call — the registry may have been mid-mutation","Audit for concurrent registration/unregistration and serialize registry access","If using a custom ToolRegistry/CapabilityRegistry, ensure get_manifests() is consistent with resolution lookups"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"contracts = {c[\"name\"] for c in app.get_capability_contracts()}\nif name not in contracts:\n    # registry in flux or stale; refresh/retry registration\n    ...","typeGuard":null,"tryCatchPattern":"for attempt in range(3):\n    try:\n        return app.get_capability_contract(name)\n    except ValueError:\n        time.sleep(0.2 * (attempt + 1))\nraise","preventionTips":["Avoid registering/unregistering capabilities concurrently with lookups","Serialize registry mutations behind a lock in custom registries"],"tags":["capability","race-condition","registry","deeptutor"],"backgroundTag":"registry-lookup-race","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}