The test request was using maxOutputTokens: 1, which caused Google API to generate only 1 token. When decoded, this single token produced "It" as the response, making it look like an error. Changed: - Content: "." → "Test connection" (more meaningful prompt) - MaxTokens: 1 → 10 (enough tokens to verify connection is working) This fixes the issue where account test always showed "It" in the response, which was actually just the truncated output from the single-token generation. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
5.4 KiB
5.4 KiB
🔍 上游 API 返回值诊断指南
当你的 Antigravity 账号验证返回 "IT" 错误时,这个错误来自上游 Google API的响应。
📊 错误链追踪
你的 curl 请求
↓
HTTP Handler (account_handler.go:671)
↓
AccountTestService.testAntigravityAccountConnection()
├─ 调用: AntigravityGatewayService.TestConnection()
│ ├─ 调用: client.LoadCodeAssist(ctx, accessToken)
│ │ ↓
│ │ 🌐 Google API (真实的上游服务器)
│ │ 返回: ??? (这是问题所在)
│ │
│ └─ 错误处理: 什么时候会返回 "IT"?
│
└─ sendErrorAndEnd(c, error_message)
↓
SSE 响应流
↓
你的 curl 看到: "IT"
🎯 上游可能返回的错误
场景 1: Access Token 无效 (最可能)
Google API 返回:
HTTP/1.1 401 Unauthorized
{
"error": {
"code": 401,
"message": "Invalid authentication credentials",
"errors": [
{
"message": "Invalid authentication credentials",
"domain": "global",
"reason": "authenticationRequired"
}
]
}
}
在你的应用中显示为: "IT"(被截断的错误信息)
场景 2: 项目配置错误
Google API 返回:
HTTP/1.1 400 Bad Request
{
"error": {
"code": 400,
"message": "The project does not have permission to call CloudAI APIs",
"errors": [...]
}
}
在你的应用中显示为: "IT"(也可能是 "Th" 或其他前两个字符)
场景 3: 模型不可用
Google API 返回:
HTTP/1.1 429 Too Many Requests
{
"error": {
"code": 429,
"message": "The resource has been exhausted.",
"errors": [...]
}
}
场景 4: 内部服务器错误
Google API 返回:
HTTP/1.1 500 Internal Server Error
{
"error": {
"code": 500,
"message": "Internal error occurred.",
"errors": [...]
}
}
🔧 如何看到真实的上游返回值
方法 A: 添加诊断日志 (推荐)
编辑 antigravity_gateway_service.go,在 TestConnection 函数中:
func (s *AntigravityGatewayService) TestConnection(ctx context.Context, account *Account, modelID string) (*TestConnectionResult, error) {
// ... 现有代码 ...
result, err := s.antigravityRetryLoop(p)
if err != nil {
// 添加这些行来捕获完整的上游错误信息
log.Printf("[UPSTREAM_ERROR] Type=%T", err)
log.Printf("[UPSTREAM_ERROR] Message=%s", err.Error())
log.Printf("[UPSTREAM_ERROR] FullError=%#v", err)
// 如果是 HTTP 错误,打印更详细的信息
if httpErr, ok := err.(interface{ StatusCode() int }); ok {
log.Printf("[UPSTREAM_ERROR] StatusCode=%d", httpErr.StatusCode())
}
return nil, err
}
// ... 继续 ...
}
然后查看日志:
# Docker 日志
docker logs <container-id> | grep "UPSTREAM_ERROR"
# 或本地日志
tail -f /var/log/sub2api/server.log | grep "UPSTREAM_ERROR"
方法 B: 使用网络抓包工具
启动 Charles/Fiddler,拦截 HTTPS 请求:
- 配置你的应用使用代理
- 运行测试请求
- 在代理工具中观察:
- Request: 发送给 Google API 的请求
- Response: Google API 返回的完整响应
方法 C: 查看应用日志中的错误
在 sendErrorAndEnd 中添加日志:
func (s *AccountTestService) sendErrorAndEnd(c *gin.Context, msg string) error {
log.Printf("[SEND_ERROR_START]")
log.Printf("[SEND_ERROR_MESSAGE_LEN]=%d", len(msg))
log.Printf("[SEND_ERROR_MESSAGE]=%q", msg) // 用 %q 显示完整的字符串(含转义)
log.Printf("[SEND_ERROR_BYTES]=%v", []byte(msg))
log.Printf("[SEND_ERROR_END]")
s.sendEvent(c, TestEvent{
Type: "test_error",
Error: msg,
Success: false,
})
s.sendEvent(c, TestEvent{Type: "test_complete", Success: false})
return nil
}
📝 真实的错误示例
示例 1: Token 过期
完整错误链:
Google API 返回 401 + "Invalid authentication credentials"
↓ (在 Client 中解析)
Go error: "Invalid authentication credentials"
↓ (在 TestConnection 中传播)
sendErrorAndEnd() 接收: "Invalid authentication credentials"
↓ (截断?编码错误?)
SSE 事件中显示: "IT" 或 "In" 或 "I"
示例 2: Project 配置错误
完整错误链:
Google API 返回 400 + "The project does not have permission..."
↓
sendErrorAndEnd() 接收: "The project does not have permission..."
↓
截断为前两个字符: "Th" ← 这与你看到的 "IT" 不符,说明不是这个
❓ 为什么会显示 "IT"?
最可能的解释:
- 错误被截断 - 原文可能是
INTERNAL_ERROR被截断成IT - 错误代码 - 某些错误被转换成了短代码
IT - 部分响应 - 只有响应的一部分被返回
✅ 下一步行动
- 立即: 添加上述诊断日志
- 运行: 执行你的测试 curl 命令
- 检查: 查看应用日志
- 记录: 复制完整的错误信息给我
📌 检查清单
- 添加了 TestConnection 的诊断日志
- 添加了 sendErrorAndEnd 的诊断日志
- 重新编译并部署应用
- 执行了测试 curl 命令
- 检查了应用日志
- 记录了完整的
[UPSTREAM_ERROR]或[SEND_ERROR]输出
完成后,请将日志输出分享给我,我们就能找到真实的错误原因!