chenhg5/cc-connect · critical

qq: ws connect failed (%s): %w

Error message

qq: ws connect failed (%s): %w

What it means

Returned by Platform.Start when the WebSocket dial to the OneBot v11 server (p.wsURL) fails. The QQ platform adapter connects via websocket.DefaultDialer.Dial; any dial-layer failure (TCP, TLS, HTTP handshake, auth rejection) is wrapped with the target URL for debugging. It aborts platform startup, so the QQ platform cannot receive or send messages.

Source

Thrown at platform/qq/qq.go:81

		allowFrom:             allowFrom,
		shareSessionInChannel: shareSessionInChannel,
		httpURL:            httpURL,
	}, nil
}

func (p *Platform) Name() string { return "qq" }

func (p *Platform) Start(handler core.MessageHandler) error {
	p.handler = handler

	header := http.Header{}
	if p.token != "" {
		header.Set("Authorization", "Bearer "+p.token)
	}

	conn, _, err := websocket.DefaultDialer.Dial(p.wsURL, header)
	if err != nil {
		return fmt.Errorf("qq: ws connect failed (%s): %w", p.wsURL, err)
	}
	p.conn = conn

	slog.Info("qq: connected to OneBot", "url", p.wsURL)

	ctx, cancel := context.WithCancel(context.Background())
	p.cancel = cancel

	// Start readLoop BEFORE callAPI: callAPI's response is routed by readLoop,
	// so calling it first would always time out after 15s and leave selfID=0,
	// which disables the self-message filter in handleMessage and lets the bot
	// respond to its own messages.
	go p.readLoop(ctx)

	// Get bot self info
	if info, err := p.callAPI("get_login_info", nil); err == nil {
		if uid, ok := info["user_id"].(float64); ok {
			p.selfID = int64(uid)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Verify the OneBot server is running and reachable: curl the HTTP endpoint or open the ws URL in a WS client.
  2. Check the qq section of config.toml: ws_url host, port, and scheme (ws:// vs wss://) must match the OneBot server config.
  3. If the server requires an access token, confirm p.token matches the OneBot access_token exactly (it is sent as 'Authorization: Bearer <token>').
  4. Inspect the wrapped inner error (%w) for the root cause: connection refused means server down; 401/403 means token mismatch; timeout means network/firewall.
  5. If using wss through a reverse proxy, ensure the proxy forwards the Upgrade and Connection headers.

Example fix

// before
ws_url = "ws://127.0.0.1:6700"   // server actually on 3001

// after
ws_url = "ws://127.0.0.1:3001"
access_token = "my-onebot-token"  // must match OneBot access_token
Defensive patterns

Strategy: retry

Validate before calling

u, err := url.Parse(cfg.QQ.WSURL)
if err != nil || (u.Scheme != "ws" && u.Scheme != "wss") {
    return fmt.Errorf("invalid qq ws_url: %q", cfg.QQ.WSURL)
}
conn, err := net.DialTimeout("tcp", u.Host, 5*time.Second)
if err != nil {
    return fmt.Errorf("onebot server unreachable at %s: %w", u.Host, err)
}
conn.Close()

Try / catch

err := p.Start(ctx)
if err != nil && strings.Contains(err.Error(), "ws connect failed") {
    slog.Warn("qq ws dial failed, retrying with backoff", "err", err)
    time.Sleep(backoff)
    return p.Start(ctx)
}

Prevention

When it happens

Trigger: Calling Start() when the OneBot server is unreachable at p.wsURL (wrong host/port, server down), the URL scheme is wrong (ws vs wss), the reverse-proxy returns non-101, or the Bearer token header is rejected and the handshake is refused.

Common situations: config.toml has the wrong ws_url for the OneBot/NapCat/Lagrange server; the OneBot service is not running or is listening on a different port; running behind a proxy that rejects WebSocket upgrades; expired or incorrect access token; firewall blocking the connection.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/0158975a1a2201a4. Report an issue: GitHub.