{"record":{"id":"7cda14acc84337cb","repo":"chenhg5/cc-connect","slug":"wait-outgoing-w","errorCode":null,"errorMessage":"wait outgoing: %w","messagePattern":"wait outgoing: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/engine.go","lineNumber":305,"sourceCode":"\t\treturn fmt.Errorf(\"platform %q does not support ReconstructReplyCtx\", req.Platform)\n\t}\n\trctx, err := rc.ReconstructReplyCtx(req.SessionKey)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"reconstruct reply ctx: %w\", err)\n\t}\n\ttext := e.i18n.T(MsgRestartSuccess)\n\tif CurrentVersion != \"\" {\n\t\ttext += fmt.Sprintf(\" (%s)\", CurrentVersion)\n\t}\n\n\tbackoffs := []time.Duration{0, 500 * time.Millisecond, 1500 * time.Millisecond}\n\tvar lastErr error\n\tfor attempt, wait := range backoffs {\n\t\tif wait > 0 {\n\t\t\ttime.Sleep(wait)\n\t\t}\n\t\tif err := e.waitOutgoing(p); err != nil {\n\t\t\tlastErr = fmt.Errorf(\"wait outgoing: %w\", err)\n\t\t\tslog.Warn(\"restart notify: wait outgoing failed\",\n\t\t\t\t\"platform\", req.Platform, \"attempt\", attempt+1, \"error\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif err := p.Send(e.ctx, rctx, text); err != nil {\n\t\t\tlastErr = err\n\t\t\tslog.Warn(\"restart notify: send failed, will retry\",\n\t\t\t\t\"platform\", req.Platform, \"session\", req.SessionKey,\n\t\t\t\t\"attempt\", attempt+1, \"max_attempts\", len(backoffs), \"error\", err)\n\t\t\tcontinue\n\t\t}\n\t\tif attempt > 0 {\n\t\t\tslog.Info(\"restart notify: send succeeded after retry\",\n\t\t\t\t\"platform\", req.Platform, \"session\", req.SessionKey, \"attempt\", attempt+1)\n\t\t}\n\t\treturn nil\n\t}\n\treturn lastErr","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/engine.go#L287-L323","documentation":"This error wraps a failure of Engine.waitOutgoing during a restart notification send loop. Before sending the 'restart succeeded' text, the engine waits for the platform's outgoing-message queue to drain; waitOutgoing failing (timeout or platform client not accepting outgoing messages) produces 'wait outgoing: %w'. The loop retries up to 3 times with backoff (0ms, 500ms, 1500ms); if every attempt fails, the last wrapped error is returned.","triggerScenarios":"Inside dispatchRestartNotify's retry loop, e.waitOutgoing(p) returns an error on all attempts — outgoing queue never drains after restart, platform client's send channel is closed/saturated, or the platform reports it cannot accept outbound messages yet.","commonSituations":"Immediately after process re-exec the platform adapter reconnects but its outgoing queue is still blocked; a flood of queued messages delays draining past the wait timeout; platform connection (websocket/long-poll) not yet re-established when the notify fires.","solutions":["Read the wrapped inner error to determine whether it is a timeout or a closed/broken connection; reconnect the platform adapter if needed.","Increase the backoff window or waitOutgoing timeout if the platform is slow to drain after restart (large queued backlog).","Ensure the platform Start() has fully completed before triggering /restart so the outgoing path is ready when the notify fires.","Retry after the platform reconnects — this is transient in most restart scenarios.","If the target chat is unreachable (bot removed), the send will keep failing; verify chat reachability (see reconstruct error) instead of tuning waits."],"exampleFix":"// before: notify fails because outgoing queue not ready right after re-exec\n// after: give the platform a grace period before the notify loop\nfunc afterRestart(p core.Platform) {\n    time.Sleep(2 * time.Second) // let adapter re-establish connection\n    engine.NotifyRestart(req)\n}","handlingStrategy":"retry","validationCode":"// Go: probe that the platform can accept outgoing messages before restart\nif err := engine.WaitOutgoing(platform); err != nil {\n    log.Printf(\"outgoing path not ready, defer restart: %v\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := engine.NotifyRestart(req); err != nil {\n    if strings.Contains(err.Error(), \"wait outgoing\") {\n        // already retried 3x internally; schedule a later manual re-notify\n        slog.Warn(\"outgoing queue never drained after restart\", \"err\", err)\n    }\n}","preventionTips":["Ensure platform Start() completes (connection established) before invoking /restart.","Avoid restarting while a large outgoing backlog is pending; drain first.","Keep the backoff window generous enough for slow platforms.","Verify network/websocket connectivity of the platform adapter after re-exec."],"tags":["restart","outgoing-queue","timeout","retry"],"backgroundTag":"request-timeout","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"}