{"record":{"id":"1712d364c1dc3652","repo":"JuliusBrussee/caveman","slug":"expected-a-native-toolselection","errorCode":null,"errorMessage":"Expected a native ToolSelection","messagePattern":"Expected a native ToolSelection","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/middleware/python/caveman_middleware/llama_index.py","lineNumber":181,"sourceCode":"        return bool(self.binding is not None and invocation is not None and invocation.registration is self\n            and invocation.recovery_allowed and len(tools) == len(self.tools)\n            and all(left is right for left, right in zip(tools, self.tools))\n            and tuple(tool.metadata.name for tool in tools) == self.names\n            and self.runtime.owns_binding(self.binding, self.scope)\n            and self.runtime.owns_binding(self.async_binding, self.scope)\n            and self.tool.fn is self.sync and self.tool.async_fn is self.async_\n            and self.tool.metadata is self.metadata and not self.tool.partial_params and not self.tool.requires_context\n            and self.metadata.description == RECOVERY_DESCRIPTION and not self.metadata.return_direct\n            and self.metadata.get_parameters_dict() == RECOVERY_SCHEMA)\n\n    def invocation(self):\n        return _Invocation(self, self.scope, self.tools, self.successful, True)\n\n    def selected(self, call: ToolSelection):\n        if self.binding is not None and not self.registered(self.tools, self.invocation()):\n            raise ValueError(\"Native executor registration changed\")\n        if not isinstance(call, ToolSelection) or not isinstance(call.tool_id, str) or not call.tool_id or not isinstance(call.tool_kwargs, dict):\n            raise TypeError(\"Expected a native ToolSelection\")\n        matches = [tool for tool in self.tools if tool.metadata.name == call.tool_name]\n        if len(matches) != 1:\n            raise ValueError(\"Native tool is not registered\")\n        self.successful.pop(call.tool_id, None)\n        return matches[0]\n\n    def completed(self, call, result):\n        if not isinstance(result, ToolOutput):\n            raise TypeError(\"Native FunctionTool returned an unexpected output\")\n        if not result.is_error and call.tool_name != \"caveman_retrieve\" and len(self.successful) < 4096:\n            self.successful[call.tool_id] = call.tool_name\n        return result\n\n\n@dataclass(frozen=True)\nclass CavemanLLMTools:\n    \"\"\"Native model and tools for an application-owned loop; no scheduler.\"\"\"\n    model: LLM","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/middleware/python/caveman_middleware/llama_index.py#L163-L199","documentation":"TypeError from the LlamaIndex recovery registration check in selected(): the object passed as the tool call is not a native llama_index ToolSelection (or its tool_id/tool_kwargs have the wrong type/shape). Only genuine native selections can drive recovery, so anything else is rejected before dispatch.","triggerScenarios":"Calling selected() with None, a raw dict, a ToolCall of another framework, a tool_id that is empty or not a str, or tool_kwargs that is None/list.","commonSituations":"Bridging from another agent framework's tool-call shape, hand-constructing ToolSelection objects, or deserialization yielding wrong types.","solutions":["Pass the actual ToolSelection object produced by the LlamaIndex agent loop","Ensure tool_id is a non-empty string and tool_kwargs is a dict before calling","If bridging frameworks, construct a proper ToolSelection from the foreign call"],"exampleFix":"// before\nexecutor.selected({\"tool_id\": None, \"tool_kwargs\": None})\n// after\nexecutor.selected(ToolSelection(tool_id=call.id, tool_name=call.name, tool_kwargs=call.arguments or {}))","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"from llama_index.core.tools.types import ToolSelection\ndef is_native_selection(call) -> bool:\n    return (isinstance(call, ToolSelection)\n            and isinstance(getattr(call, \"tool_id\", None), str)\n            and bool(call.tool_id)\n            and isinstance(getattr(call, \"tool_kwargs\", None), dict))","tryCatchPattern":"try:\n    tool = executor.selected(call)\nexcept TypeError as e:\n    log.error(\"bad ToolSelection: %s\", e)\n    raise","preventionTips":["Only pass ToolSelection objects produced by the LlamaIndex agent loop","Validate tool_id and tool_kwargs when bridging from other frameworks","Avoid hand-rolled dict stand-ins for ToolSelection"],"tags":["python","type-error","llamaindex"],"backgroundTag":"type-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}