chenhg5/cc-connect · error
expected READY event, got op=%d t=%s
Error message
expected READY event, got op=%d t=%s
What it means
Returned when a dispatch frame arrives but is not the expected READY event (op 0 with t=READY). The server sent a different dispatch — commonly RESUMED, an error event, or a heartbeart-adjacent op — so the session ID cannot be captured.
Source
Thrown at platform/qqbot/qqbot.go:770
"shard": [2]int{0, 1},
},
}
p.wsMu.Lock()
err := conn.WriteJSON(identify)
p.wsMu.Unlock()
return err
}
func (p *Platform) waitForReady(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 ready: %w", err)
}
if msg.Op != opDispatch || msg.T != "READY" {
return fmt.Errorf("expected READY event, got op=%d t=%s", msg.Op, msg.T)
}
var ready struct {
SessionID string `json:"session_id"`
}
if err := json.Unmarshal(msg.D, &ready); err != nil {
slog.Warn("qqbot: failed to parse READY payload", "error", err)
}
p.sessionID = ready.SessionID
if msg.S != nil {
p.lastSeq.Store(*msg.S)
}
slog.Info("qqbot: gateway READY", "session_id", p.sessionID)
return nil
}
func (p *Platform) heartbeatLoop(ctx context.Context) {View on GitHub (pinned to 4000b2338a)
Solutions
- Log msg.Op and msg.T of the unexpected frame for diagnosis
- Clear any persisted session_id and perform a fresh Identify instead of resume
- Validate the intents and token in the identify payload
- Update event-name expectations if QQ renamed READY/RESUMED semantics
Example fix
// before
if msg.Op != opDispatch || msg.T != "READY" {
return fmt.Errorf("expected READY event, got op=%d t=%s", msg.Op, msg.T)
}
// after
if msg.Op != opDispatch || msg.T != "READY" {
return fmt.Errorf("expected READY event, got op=%d t=%s data=%s", msg.Op, msg.T, msg.D)
} Defensive patterns
Strategy: fallback
Type guard
func isReadyDispatch(msg wsPayload) bool { return msg.Op == opDispatch && msg.T == "READY" } Try / catch
if err := p.waitForReady(conn); err != nil {
if strings.Contains(err.Error(), "got op=") {
slog.Warn("non-READY dispatch, forcing fresh identify", "err", err)
return p.identifyFresh() // drop stale session_id
}
return err
} Prevention
- Do not persist session_id across restarts unless resuming deliberately
- Log op and t of every dispatch during handshake
- Handle RESUMED and error dispatches explicitly instead of failing
When it happens
Trigger: After sending Identify, the first dispatch is not READY: server sends RESUMED, a dispatch for a stale session, or an error event because the identify payload was rejected.
Common situations: Stale session_id being resumed unexpectedly; intents mismatch so QQ rejects identify via a dispatch error; protocol changes in event names.
Related errors
- expected op 10 (Hello), got op %d
- cloud_web: expected register_ack, got %q
- waiting for hello: %w
- waiting for ready: %w
- codex app-server initialize: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/072895ea4988d68b.
Report an issue: GitHub.