{"record":{"id":"072895ea4988d68b","repo":"chenhg5/cc-connect","slug":"expected-ready-event-got-op-d-t-s","errorCode":null,"errorMessage":"expected READY event, got op=%d t=%s","messagePattern":"expected READY event, got op=(.+?) t=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/qqbot/qqbot.go","lineNumber":770,"sourceCode":"\t\t\t\"shard\":   [2]int{0, 1},\n\t\t},\n\t}\n\tp.wsMu.Lock()\n\terr := conn.WriteJSON(identify)\n\tp.wsMu.Unlock()\n\treturn err\n}\n\nfunc (p *Platform) waitForReady(conn *websocket.Conn) error {\n\t_ = conn.SetReadDeadline(time.Now().Add(15 * time.Second))\n\tdefer func() { _ = conn.SetReadDeadline(time.Time{}) }()\n\n\tvar msg wsPayload\n\tif err := conn.ReadJSON(&msg); err != nil {\n\t\treturn fmt.Errorf(\"waiting for ready: %w\", err)\n\t}\n\tif msg.Op != opDispatch || msg.T != \"READY\" {\n\t\treturn fmt.Errorf(\"expected READY event, got op=%d t=%s\", msg.Op, msg.T)\n\t}\n\n\tvar ready struct {\n\t\tSessionID string `json:\"session_id\"`\n\t}\n\tif err := json.Unmarshal(msg.D, &ready); err != nil {\n\t\tslog.Warn(\"qqbot: failed to parse READY payload\", \"error\", err)\n\t}\n\tp.sessionID = ready.SessionID\n\tif msg.S != nil {\n\t\tp.lastSeq.Store(*msg.S)\n\t}\n\n\tslog.Info(\"qqbot: gateway READY\", \"session_id\", p.sessionID)\n\treturn nil\n}\n\nfunc (p *Platform) heartbeatLoop(ctx context.Context) {","sourceCodeStart":752,"sourceCodeEnd":788,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/qqbot/qqbot.go#L752-L788","documentation":"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.","triggerScenarios":"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.","commonSituations":"Stale session_id being resumed unexpectedly; intents mismatch so QQ rejects identify via a dispatch error; protocol changes in event names.","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"],"exampleFix":"// before\nif msg.Op != opDispatch || msg.T != \"READY\" {\n    return fmt.Errorf(\"expected READY event, got op=%d t=%s\", msg.Op, msg.T)\n}\n// after\nif msg.Op != opDispatch || msg.T != \"READY\" {\n    return fmt.Errorf(\"expected READY event, got op=%d t=%s data=%s\", msg.Op, msg.T, msg.D)\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":"func isReadyDispatch(msg wsPayload) bool { return msg.Op == opDispatch && msg.T == \"READY\" }","tryCatchPattern":"if err := p.waitForReady(conn); err != nil {\n    if strings.Contains(err.Error(), \"got op=\") {\n        slog.Warn(\"non-READY dispatch, forcing fresh identify\", \"err\", err)\n        return p.identifyFresh() // drop stale session_id\n    }\n    return err\n}","preventionTips":["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"],"tags":["websocket","protocol","handshake","qqbot"],"backgroundTag":"unexpected-response-shape","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}