PrefectHQ/fastmcp · error · ValueError

Component already exists: {component.key}

Error message

Component already exists: {component.key}

What it means

LocalProvider was configured with on_duplicate='error', and a component with the same key (type + identifier + version) is already registered. Adding it again raises ValueError including the duplicate key, instead of silently replacing or ignoring.

Source

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

                        f"Cannot add unversioned {type_name} {logical_name!r}: "
                        f"versioned {type_name}s with this name already exist "
                        f"(e.g., version={existing.version!r}). "
                        f"Either version all components or none."
                    )

    def _add_component(self, component: _C) -> _C:
        """Add a component to unified storage.

        Args:
            component: The component to add.

        Returns:
            The component that was added (or existing if on_duplicate="ignore").
        """
        existing = self._components.get(component.key)
        if existing:
            if self._on_duplicate == "error":
                raise ValueError(f"Component already exists: {component.key}")
            elif self._on_duplicate == "warn":
                logger.warning(f"Component already exists: {component.key}")
            elif self._on_duplicate == "ignore":
                return existing  # type: ignore[return-value]  # ty:ignore[invalid-return-type]
            # "replace" and "warn" fall through to add

        # 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.

View on GitHub (pinned to 1f02114297)

Solutions

  1. Set on_duplicate='replace' (or 'ignore'/'warn') when constructing the provider/server to permit re-registration.
  2. Guard registration so each component is added only once (idempotent init, module-level once-guard).
  3. Bump or vary the component version/name if the duplicate is genuinely a distinct variant.

Example fix

// before
mcp = FastMCP()  # on_duplicate defaults to 'error'
mcp.add_tool(my_tool)
mcp.add_tool(my_tool)  # raises

// after
from fastmcp.server.providers import LocalProvider
provider = LocalProvider(on_duplicate='replace')
mcp.add_provider(provider)
mcp.add_tool(my_tool)
mcp.add_tool(my_tool)  # replaces
Defensive patterns

Strategy: try-catch

Validate before calling

existing_keys = {c.key for c in provider._components.values()}
if my_tool.key in existing_keys:
    logging.warning('skipping duplicate registration for %s', my_tool.key)
else:
    mcp.add_tool(my_tool)

Type guard

def already_registered(provider, component) -> bool:
    return component.key in {c.key for c in provider._components.values()}

Try / catch

try:
    mcp.add_tool(my_tool)
except ValueError as e:
    if str(e).startswith('Component already exists'):
        logging.info('tool already registered: %s', e)
    else:
        raise

Prevention

When it happens

Trigger: Calling add_tool/add_resource/add_template/add_prompt twice with the same name/uri/version while the provider's on_duplicate policy is 'error'; re-importing/re-running a registration module against a long-lived server object.

Common situations: Module-level registrations executed twice (hot reload, notebook re-runs, tests reusing a server fixture); plugin systems registering the same component from two files.

Related errors


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