sipeed/picoclaw · error
failed to get websocket info: %w
Error message
failed to get websocket info: %w
What it means
QQChannel.Start calls c.api.WS(c.ctx, nil, "") on the botgo OpenAPI client to fetch the WebSocket gateway URL for the bot's session manager. When that HTTP call fails — auth rejected, network error, or QQ API 5xx — the error is wrapped as "failed to get websocket info". Until this succeeds the channel has no gateway and cannot receive events.
Source
Thrown at pkg/channels/qq/qq.go:141
// start auto-refresh token goroutine
if err := token.StartRefreshAccessToken(c.ctx, c.tokenSource); err != nil {
return fmt.Errorf("failed to start token refresh: %w", err)
}
// initialize OpenAPI client
c.api = botgo.NewOpenAPI(c.config.AppID, c.tokenSource).WithTimeout(5 * time.Second)
// register event handlers
intent := event.RegisterHandlers(
c.handleC2CMessage(),
c.handleGroupATMessage(),
)
// get WebSocket endpoint
wsInfo, err := c.api.WS(c.ctx, nil, "")
if err != nil {
return fmt.Errorf("failed to get websocket info: %w", err)
}
logger.InfoCF("qq", "Got WebSocket info", map[string]any{
"shards": wsInfo.Shards,
})
// create and save sessionManager
c.sessionManager = botgo.NewSessionManager()
// start WebSocket connection in goroutine to avoid blocking
go func() {
if err := c.sessionManager.Start(wsInfo, c.tokenSource, &intent); err != nil {
logger.ErrorCF("qq", "WebSocket session error", map[string]any{
"error": err.Error(),
})
c.SetRunning(false)
}
}()View on GitHub (pinned to 49183d7e8d)
Solutions
- Inspect the wrapped error (errors.Unwrap / %v chain) to tell auth failure (fix credentials) from network failure (fix connectivity).
- Retry Start with backoff — the WS info call is a plain HTTP GET and commonly succeeds on the second attempt after a transient failure.
- Verify the bot application is published/active in the QQ console (unactivated apps can be denied gateway access).
- Check egress rules allow https and wss to the QQ open platform endpoints from the host/container.
Example fix
// before: single-shot start
if err := qqCh.Start(ctx); err != nil { return err }
// after: retry transient gateway-lookup failures with backoff
var err error
for attempt := 0; attempt < 3; attempt++ {
if err = qqCh.Start(ctx); err == nil { break }
if strings.Contains(err.Error(), "websocket info") { // transient path
time.Sleep(time.Duration(attempt+1) * 2 * time.Second)
continue
}
break
} Defensive patterns
Strategy: retry
Validate before calling
// cheap connectivity gate before Start
if _, err := net.DialTimeout("tcp", "api.sgroup.qq.com:443", 3*time.Second); err != nil {
return fmt.Errorf("QQ API unreachable, deferring channel start: %w", err)
} Try / catch
// transient gateway lookups: bounded retry with backoff
var err error
for i := 0; i < 3; i++ {
if err = ch.Start(ctx); err == nil || !strings.Contains(err.Error(), "websocket info") { break }
select { case <-ctx.Done(): return ctx.Err(); case <-time.After(time.Duration(i+1) * 2 * time.Second): }
} Prevention
- Treat WS-info failure as transient first, credential error second (check the wrapped cause)
- Keep egress to QQ openapi hosts allowed in firewall/NetworkPolicy
- Monitor Start failures and alert on repeated restarts
When it happens
Trigger: Start() invoked with an invalid/expired access token (token source returned a token the WS endpoint rejects); network failure reaching the QQ open platform API; QQ API incident returning 5xx; sandbox environments without egress to the QQ domain.
Common situations: app_secret rotated so freshly minted tokens are for the wrong credentials; corporate proxies blocking WebSocket upgrade discovery; QQ open platform maintenance windows; intermittent DNS failures at startup causing repeated Start/Stop cycles.
Related errors
- failed to start stream client: %w
- failed to open discord session: %w
- failed to start token refresh: %w
- after %d retries: %w
- LLM call failed after retries: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/a592cad04141eff7.
Report an issue: GitHub.