PrefectHQ/fastmcp · error · KeyError
Resource {uri!r} not found
Error message
Resource {uri!r} not found What it means
remove_resource(uri, version=None) scans LocalProvider storage for Resource components whose string URI matches; when no match is found it raises KeyError(f"Resource {uri!r} not found"). It signals the caller that removal found nothing to delete.
Source
Thrown at fastmcp_slim/fastmcp/server/providers/local_provider/local_provider.py:275
def remove_resource(self, uri: str, version: str | None = None) -> None:
"""Remove resource(s) from this provider's storage.
Args:
uri: The resource URI.
version: If None, removes ALL versions. If specified, removes only that version.
Raises:
KeyError: If no matching resource is found.
"""
if version is None:
# Remove all versions
keys_to_remove = [
k
for k, c in self._components.items()
if isinstance(c, Resource) and str(c.uri) == uri
]
if not keys_to_remove:
raise KeyError(f"Resource {uri!r} not found")
for key in keys_to_remove:
self._remove_component(key)
else:
# Remove specific version
key = f"{Resource.make_key(uri)}@{version}"
if key not in self._components:
raise KeyError(f"Resource {uri!r} version {version!r} not found")
self._remove_component(key)
def remove_template(self, uri_template: str, version: str | None = None) -> None:
"""Remove resource template(s) from this provider's storage.
Args:
uri_template: The template URI pattern.
version: If None, removes ALL versions. If specified, removes only that version.
Raises:
KeyError: If no matching template is found.View on GitHub (pinned to 1f02114297)
Solutions
- Compare the exact str(resource.uri) as registered — check for scheme/slash/case differences first
- List existing resources to confirm the exact URI before removal
- Catch KeyError to make removal idempotent
Example fix
// before
provider.remove_resource("config://app/") # registered as "config://app"
// after
provider.remove_resource("config://app") # exact registered URI Defensive patterns
Strategy: try-catch
Validate before calling
uris = {str(c.uri) for c in provider._components.values() if isinstance(c, Resource)}
if "config://app" not in uris:
return Type guard
def resource_exists(provider, uri: str) -> bool:
return any(isinstance(c, Resource) and str(c.uri) == uri
for c in provider._components.values()) Try / catch
try:
provider.remove_resource("config://app")
except KeyError:
pass # idempotent teardown Prevention
- Pass the URI exactly as str(resource.uri) was registered
- Watch for trailing-slash and scheme normalization differences
- Keep resource URIs as constants shared between registration and removal
When it happens
Trigger: provider.remove_resource("config://app") when the resource URI differs (scheme, trailing slash, normalization), was never added, was already removed, or lives in another provider.
Common situations: URI stringification mismatches (AnyUrl vs str, case, trailing slash); removing a resource mounted on a different provider; refactoring URIs without updating removal code.
Related errors
- Resource {uri!r} version {version!r} not found
- Tool {name!r} not found
- Tool {name!r} version {version!r} not found
- Template {uri_template!r} not found
- Template {uri_template!r} version {version!r} not found
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/fb9ace4a73e4d5a9.
Report an issue: GitHub.