PrefectHQ/fastmcp · error · KeyError

Component {key!r} not found

Error message

Component {key!r} not found

What it means

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.

Source

Thrown at fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py:214

        # Check for versioned/unversioned mixing before adding
        self._check_version_mixing(component)

        self._components[component.key] = component
        return component

    def _remove_component(self, key: str) -> None:
        """Remove a component from unified storage.

        Args:
            key: The prefixed key of the component.

        Raises:
            KeyError: If the component is not found.
        """
        component = self._components.get(key)
        if component is None:
            raise KeyError(f"Component {key!r} not found")

        del self._components[key]

    def _get_component(self, key: str) -> FastMCPComponent | None:
        """Get a component by its prefixed key.

        Args:
            key: The prefixed key (e.g., "tool:name", "resource:uri").

        Returns:
            The component, or None if not found.
        """
        return self._components.get(key)

    def remove_tool(self, name: str, version: str | None = None) -> None:
        """Remove tool(s) from this provider's storage.

        Args:

View on GitHub (pinned to 1f02114297)

Solutions

  1. Verify the exact key with provider._get_component(key) or by listing components before removal
  2. Catch KeyError and treat as a no-op if idempotent removal is intended
  3. Use the public remove_tool/remove_resource/remove_template/remove_prompt APIs instead, which accept names/URIs and give more descriptive errors

Example fix

// before
provider._remove_component("tool:greet")  # KeyError if not present
// after
if provider._get_component("tool:greet") is not None:
    provider._remove_component("tool:greet")
Defensive patterns

Strategy: try-catch

Validate before calling

key = "tool:my_tool"
if provider._get_component(key) is None:
    return  # nothing to remove

Type guard

def has_component(provider, key: str) -> bool:
    return provider._get_component(key) is not None

Try / catch

try:
    provider._remove_component(key)
except KeyError:
    pass  # already absent; treat as idempotent success

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/73ef19efd64c9a54. Report an issue: GitHub.