chenhg5/cc-connect · error

expected op 10 (Hello), got op %d

Error message

expected op 10 (Hello), got op %d

What it means

Returned when the first frame received on the WebSocket is not an op 10 (Hello) dispatch, indicating the server deviated from the QQ gateway protocol. Usually the connection is being rejected or a different op (e.g. an error/close op) arrives first.

Source

Thrown at platform/qqbot/qqbot.go:726

}

type wsPayload struct {
	Op int             `json:"op"`
	D  json.RawMessage `json:"d,omitempty"`
	S  *int64          `json:"s,omitempty"`
	T  string          `json:"t,omitempty"`
}

func (p *Platform) waitForHello(conn *websocket.Conn) error {
	_ = conn.SetReadDeadline(time.Now().Add(15 * time.Second))
	defer func() { _ = conn.SetReadDeadline(time.Time{}) }()

	var msg wsPayload
	if err := conn.ReadJSON(&msg); err != nil {
		return fmt.Errorf("waiting for hello: %w", err)
	}
	if msg.Op != opHello {
		return fmt.Errorf("expected op 10 (Hello), got op %d", msg.Op)
	}

	var hello struct {
		HeartbeatInterval int `json:"heartbeat_interval"`
	}
	if err := json.Unmarshal(msg.D, &hello); err != nil {
		slog.Warn("qqbot: failed to parse Hello payload", "error", err)
	}
	if hello.HeartbeatInterval > 0 {
		p.heartbeatMs = hello.HeartbeatInterval
	} else {
		p.heartbeatMs = 41250 // sane default
	}
	p.heartbeatOK.Store(true)

	slog.Debug("qqbot: received Hello", "heartbeat_interval", p.heartbeatMs)
	return nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Log msg.Op and msg.D of the unexpected frame to identify what the server sent
  2. Refresh the access token and reconnect — invalid auth often surfaces as a wrong-op frame
  3. Update to the latest QQ gateway protocol expectations (op codes) if QQ changed them
  4. Retry; if op indicates reconnect, honor the resume flow instead of failing

Example fix

// before
if msg.Op != opHello {
    return fmt.Errorf("expected op 10 (Hello), got op %d", msg.Op)
}
// after
if msg.Op != opHello {
    return fmt.Errorf("expected op 10 (Hello), got op %d payload=%s", msg.Op, msg.D)
}
Defensive patterns

Strategy: try-catch

Type guard

func isHelloFrame(msg wsPayload) bool { return msg.Op == opHello }

Try / catch

if err := p.waitForHello(conn); err != nil {
    if strings.Contains(err.Error(), "got op") {
        slog.Error("unexpected hello frame, reconnecting", "err", err)
        return p.reconnect()
    }
    return err
}

Prevention

When it happens

Trigger: After dialing, the server sends a frame whose op code is not opHello — e.g. op 9 (invalid session), an error payload, or a reconnect request instead of Hello.

Common situations: QQ protocol/API version drift; connecting with an invalid token causing a non-Hello rejection frame; running an outdated bot SDK against a changed gateway.

Related errors


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