jd-opensource/joyagent-jdgenie · error · Exception

认证失败 - 无效的凭据

Error message

认证失败 - 无效的凭据

What it means

Raised by `_sse_connection` in the MCP SSE client when the server responds with an authentication failure (401) during the SSE handshake. The original exception is inspected by `_is_authentication_error` and re-raised as a generic Exception with this message. It means the configured credentials (API key/token) are missing, malformed, or rejected by the server.

Solutions

  1. Verify the API key/token configured on the client (constructor/headers) is current and correct for this server_url
  2. Check the Authorization header is actually being attached (log or inspect the client config before connect)
  3. Curl the SSE endpoint manually with the same header to confirm the credential works outside the library
  4. Re-generate or rotate credentials from the server admin console and restart the client

Example fix

// before
client = SseClient(server_url=URL)  # no auth headers
// after
client = SseClient(server_url=URL, headers={"Authorization": f"Bearer {os.environ['MCP_TOKEN']}"})
Defensive patterns

Strategy: try-catch

Validate before calling

token = os.environ.get("MCP_TOKEN")
if not token:
    raise RuntimeError("MCP_TOKEN not set")
# optionally: curl the endpoint with the header before connecting

Type guard

def has_credentials(client) -> bool:
    return bool(getattr(client, "headers", {}).get("Authorization"))

Try / catch

try:
    await client.ping_server()
except Exception as e:
    if "认证失败" in str(e):
        refresh_credentials_and_retry()
    else:
        raise

Prevention

When it happens

Trigger: Any of ping_server, list_tools, or call_tool opens an SSE session (`_sse_connection`) while the HTTP connection is rejected with 401 Unauthorized — e.g. expired/invalid API key, wrong auth header, or server auth policy change.

Common situations: Environment variable holding the token not set or pointing at the wrong server; token rotated or revoked upstream; missing `Authorization` header configuration in the client constructor; using a staging token against production.

Related errors


AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08). Data as JSON: /api/errors/723a431e89042d88. Report an issue: GitHub.

Appendix: source

Thrown at genie-client/app/client.py:146

            streams = await self._streams_context.__aenter__()
            logger.debug(f"[{connection_id}] SSE流连接已建立")

            # 创建客户端会话
            self._session_context = ClientSession(*streams)
            session = await self._session_context.__aenter__()
            logger.debug(f"[{connection_id}] 客户端会话已创建")

            # 初始化会话,可能触发认证验证
            await session.initialize()
            logger.info(f"[{connection_id}] SSE连接建立成功")

            yield session

        except Exception as e:
            # 根据异常类型进行不同的处理
            if self._is_authentication_error(e):
                logger.error(f"[{connection_id}] 认证失败 - 401 未授权")
                raise Exception("认证失败 - 无效的凭据") from e
            elif self._is_network_error(e):
                logger.error(f"[{connection_id}] 网络连接失败: {str(e)}")
                raise Exception(f"网络连接失败: {str(e)}") from e
            else:
                logger.error(f"[{connection_id}] SSE连接失败: {str(e)}")
                raise
        finally:
            # 确保资源被正确清理
            await self._cleanup_connection(connection_id)

    @staticmethod
    def _is_authentication_error(exception: Exception) -> bool:
        """
        检查异常是否为认证错误 (401 Unauthorized)

        Args:
            exception: 待检查的异常对象

View on GitHub (pinned to 2417e0b8b6)