PrefectHQ/fastmcp · error · AuthorizationError

Authorization failed for tool '{tool_name}': insufficient pe

Error message

Authorization failed for tool '{tool_name}': insufficient permissions

What it means

Fallback branch of the global auth check in on_call_tool: the auth checks rejected the request but produced no specific scope shortfall, so a generic AuthorizationError is raised instead of InsufficientScopeError. Denial came from non-scope logic in the auth provider (role checks, custom predicates, audience validation, etc.).

Source

Thrown at fastmcp_slim/fastmcp/server/middleware/authorization.py:250

                f"Authorization failed for tool '{tool_name}': "
                "not found or not authorized"
            )

        # Global auth check
        token = get_access_token()
        ctx = AuthContext(token=token, component=tool)
        authorized, missing = await run_auth_checks_with_shortfall(self.auth, ctx)
        if not authorized:
            if missing:
                missing = self._chain_shortfall(missing, ctx, fastmcp.fastmcp)
                raise InsufficientScopeError(
                    missing,
                    message=(
                        f"Authorization failed for tool '{tool_name}': "
                        f"insufficient scope (required: {', '.join(missing)})"
                    ),
                )
            raise AuthorizationError(
                f"Authorization failed for tool '{tool_name}': insufficient permissions"
            )

        return await call_next(context)

    async def on_list_resources(
        self,
        context: MiddlewareContext[mt.ListResourcesRequest],
        call_next: CallNext[mt.ListResourcesRequest, Sequence[Resource]],
    ) -> Sequence[Resource]:
        """Filter resources/list response based on auth checks."""
        resources = await call_next(context)

        # STDIO has no auth concept, skip filtering
        from fastmcp.server.context import _current_transport

        if _current_transport.get() == "stdio":
            return resources

View on GitHub (pinned to 1f02114297)

Solutions

  1. Inspect your auth provider's checks — any check that returns False must either raise with a reason or report the missing scopes so the middleware can produce a shortfall.
  2. Decode the access token and compare its claims (roles, scopes, aud, iss) against what the tool's auth policy expects.
  3. Fix the token: obtain one with the required roles/claims, or correct the provider's claim mapping.
  4. Review global vs component auth policy so the request isn't denied by an unintended default-deny rule.

Example fix

# before
class RoleCheck(AuthCheck):
    async def check(self, ctx): return False  # denial with no shortfall -> generic error
# after
class RoleCheck(AuthCheck):
    async def check(self, ctx):
        if 'admin' not in ctx.token.roles:
            raise AuthorizationError('missing admin role')
        return True
Defensive patterns

Strategy: try-catch

Validate before calling

claims = decode_jwt(token)
assert claims.get('aud') == expected_audience, 'audience mismatch will cause generic denial'
assert required_roles.issubset(set(claims.get('roles', []))), 'token missing roles'

Try / catch

from fastmcp.exceptions import AuthorizationError
try:
    result = await client.call_tool('my_tool', args)
except AuthorizationError as e:
    if 'insufficient permissions' in str(e):
        logger.error('Denied without scope shortfall; check custom auth checks and token claims')
    else:
        raise

Prevention

When it happens

Trigger: run_auth_checks_with_shortfall returns authorized=False with an empty `missing` list for the tool's AuthContext — a custom auth check returning False without reporting a shortfall, role/permission checks failing, JWT audience/issuer mismatch, or an unintended default-deny policy.

Common situations: Custom auth providers whose check() returns False silently; tokens lacking required roles/claims (as opposed to scopes); audience/issuer mismatch on the JWT; deny-all policy accidentally applying to tools.

Related errors


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