{"record":{"id":"73ef19efd64c9a54","repo":"PrefectHQ/fastmcp","slug":"component-key-r-not-found","errorCode":null,"errorMessage":"Component {key!r} not found","messagePattern":"Component (.+?) not found","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py","lineNumber":214,"sourceCode":"\n        # Check for versioned/unversioned mixing before adding\n        self._check_version_mixing(component)\n\n        self._components[component.key] = component\n        return component\n\n    def _remove_component(self, key: str) -> None:\n        \"\"\"Remove a component from unified storage.\n\n        Args:\n            key: The prefixed key of the component.\n\n        Raises:\n            KeyError: If the component is not found.\n        \"\"\"\n        component = self._components.get(key)\n        if component is None:\n            raise KeyError(f\"Component {key!r} not found\")\n\n        del self._components[key]\n\n    def _get_component(self, key: str) -> FastMCPComponent | None:\n        \"\"\"Get a component by its prefixed key.\n\n        Args:\n            key: The prefixed key (e.g., \"tool:name\", \"resource:uri\").\n\n        Returns:\n            The component, or None if not found.\n        \"\"\"\n        return self._components.get(key)\n\n    def remove_tool(self, name: str, version: str | None = None) -> None:\n        \"\"\"Remove tool(s) from this provider's storage.\n\n        Args:","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py#L196-L232","documentation":"LocalProvider stores all components (tools, resources, templates, prompts) in a single dict keyed by prefixed keys like \"tool:name\" or \"resource:uri\". _remove_component deletes an entry by key, and raises KeyError when the key is absent from storage. It is an internal helper invoked by the public remove_tool/remove_resource/remove_template/remove_prompt methods after their own lookups.","triggerScenarios":"Directly calling provider._remove_component(key) with a key string that is not currently in the provider's _components dict — e.g. a typo in the prefixed key, a component that was already removed, or a key from a different provider.","commonSituations":"Test code or custom provider subclasses manipulating the internal dict directly; double-removal in teardown logic; using a raw name instead of the prefixed key form produced by FastMCPComponent.key.","solutions":["Verify the exact key with provider._get_component(key) or by listing components before removal","Catch KeyError and treat as a no-op if idempotent removal is intended","Use the public remove_tool/remove_resource/remove_template/remove_prompt APIs instead, which accept names/URIs and give more descriptive errors"],"exampleFix":"// before\nprovider._remove_component(\"tool:greet\")  # KeyError if not present\n// after\nif provider._get_component(\"tool:greet\") is not None:\n    provider._remove_component(\"tool:greet\")","handlingStrategy":"try-catch","validationCode":"key = \"tool:my_tool\"\nif provider._get_component(key) is None:\n    return  # nothing to remove","typeGuard":"def has_component(provider, key: str) -> bool:\n    return provider._get_component(key) is not None","tryCatchPattern":"try:\n    provider._remove_component(key)\nexcept KeyError:\n    pass  # already absent; treat as idempotent success","preventionTips":["Prefer the public remove_* APIs over the internal _remove_component","Build keys from component.key or Type.make_key(), never by hand","Make teardown/removal logic idempotent by swallowing KeyError"],"tags":["keyerror","provider","component-management"],"backgroundTag":"component-key-not-found","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}