PrefectHQ/fastmcp · error · KeyError

Prompt {name!r} version {version!r} not found

Error message

Prompt {name!r} version {version!r} not found

What it means

remove_prompt with an explicit version constructs f"{Prompt.make_key(name)}@{version}" and raises KeyError when that key is not in storage. The prompt name exists but not under the supplied version string.

Source

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

        Raises:
            KeyError: If no matching prompt is found.
        """
        if version is None:
            # Remove all versions
            keys_to_remove = [
                k
                for k, c in self._components.items()
                if isinstance(c, Prompt) and c.name == name
            ]
            if not keys_to_remove:
                raise KeyError(f"Prompt {name!r} not found")
            for key in keys_to_remove:
                self._remove_component(key)
        else:
            # Remove specific version
            key = f"{Prompt.make_key(name)}@{version}"
            if key not in self._components:
                raise KeyError(f"Prompt {name!r} version {version!r} not found")
            self._remove_component(key)

    # =========================================================================
    # Provider interface implementation
    # =========================================================================

    async def _list_tools(self) -> Sequence[Tool]:
        """Return all tools."""
        return [v for v in self._components.values() if isinstance(v, Tool)]

    async def _get_tool(
        self, name: str, version: VersionSpec | None = None
    ) -> Tool | None:
        """Get a tool by name.

        Args:
            name: The tool name.
            version: Optional version filter. If None, returns highest version.

View on GitHub (pinned to 1f02114297)

Solutions

  1. Omit the version argument to remove all versions
  2. Use the exact version string from registration
  3. Catch KeyError for idempotent teardown

Example fix

// before
provider.remove_prompt("summarize", "v2")  # registered as "2.0"
// after
provider.remove_prompt("summarize", "2.0")
Defensive patterns

Strategy: validation

Validate before calling

key = f"{Prompt.make_key('summarize')}@2.0"
if key not in provider._components:
    provider.remove_prompt("summarize")  # all versions

Type guard

def prompt_version_registered(provider, name: str, version: str) -> bool:
    return f"{Prompt.make_key(name)}@{version}" in provider._components

Try / catch

try:
    provider.remove_prompt("summarize", "2.0")
except KeyError:
    provider.remove_prompt("summarize")  # remove all versions

Prevention

When it happens

Trigger: provider.remove_prompt("summarize", "2") when the prompt is unversioned or versioned "2.0"/"v2".

Common situations: Version formatting mismatches ("2" vs "2.0" vs "v2"); removing a version already deleted; prompts registered without versions but removal code passing one.

Related errors


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