sipeed/picoclaw · error
failed to start token refresh: %w
Error message
failed to start token refresh: %w
What it means
QQChannel.Start wraps the error from token.StartRefreshAccessToken, the goroutine/loop that obtains and periodically refreshes the QQ access token using the configured app_id/app_secret. It fails when the very first token request fails — bad credentials, no network to the QQ auth endpoint, or an already-cancelled context — and the underlying error is preserved via %w for inspection.
Source
Thrown at pkg/channels/qq/qq.go:126
logger.InfoC("qq", "Starting QQ bot (WebSocket mode)")
// Reinitialize shutdown signal for clean restart.
c.done = make(chan struct{})
c.stopOnce = sync.Once{}
// create token source
credentials := &token.QQBotCredentials{
AppID: c.config.AppID,
AppSecret: c.config.AppSecret.String(),
}
c.tokenSource = token.NewQQBotTokenSource(credentials)
// create child context
c.ctx, c.cancel = context.WithCancel(ctx)
// 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{View on GitHub (pinned to 49183d7e8d)
Solutions
- Print the full chain (fmt.Printf("%+v", err) or errors.As) — the wrapped error distinguishes 401/invalid_client (bad credentials) from timeouts/DNS (network).
- Fix credentials in the QQ open platform console if the wrapped error indicates authorization failure, then restart the channel.
- Restore outbound connectivity to the QQ API host (firewall, DNS, proxy) if the wrapped error is a network class failure.
- Retry Start after a short backoff — QQ open platform occasionally returns transient 5xx on the token endpoint.
Example fix
// before: swallowing the chain
if err := ch.Start(ctx); err != nil {
log.Fatal("start failed")
}
// after: surface the wrapped cause
if err := ch.Start(ctx); err != nil {
var netErr net.Error
if errors.As(err, &netErr) {
log.Printf("qq: transient network issue, retrying: %v", err)
time.Sleep(5 * time.Second)
return ch.Start(ctx)
}
return err // credential/config problem
} Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight: can we reach the QQ token endpoint with these credentials?
if _, err := http.Head("https://bots.qq.com"); err != nil {
return fmt.Errorf("no egress to QQ API, Start will fail: %w", err)
} Try / catch
if err := ch.Start(ctx); err != nil {
var netErr net.Error
switch {
case errors.As(err, &netErr):
retryAfterBackoff() // transient
case strings.Contains(err.Error(), "failed to start token refresh"):
inspectCredentials() // unwrap with %v to see invalid_client vs timeout
}
} Prevention
- Always log the wrapped chain (%+v) — it separates credential errors from network errors
- Verify credentials with a manual token curl before enabling the channel
- Cancel-and-restart cycles: ensure the ctx passed to Start is not already cancelled
When it happens
Trigger: Calling Start with an incorrect app_secret (auth endpoint returns invalid_client); no DNS/route to openapi.qq.com or the token endpoint from the host; passing an already-cancelled context so the refresh loop exits immediately; QQ open platform outage during startup.
Common situations: Copied app_id correctly but app_secret stale after a console reset; container with no egress or wrong proxy env; clock skew breaking token issuance; restarting the channel after a network partition before connectivity is restored.
Related errors
- slack auth test failed: %w
- failed to get websocket info: %w
- reading usage response: %w
- failed to start stream client: %w
- failed to create discord session: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/1b5e3d653ebbbbbf.
Report an issue: GitHub.