{"record":{"id":"7a9dcf001679e9cf","repo":"chenhg5/cc-connect","slug":"reconstruct-reply-ctx-w","errorCode":null,"errorMessage":"reconstruct reply ctx: %w","messagePattern":"reconstruct reply ctx: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/engine.go","lineNumber":291,"sourceCode":"\t}\n\te.pendingRestartMu.Unlock()\n}\n\n// dispatchRestartNotify sends the notify to the target platform with up\n// to 3 attempts (initial + 2 retries) on transient failure. The\n// platform must already be ready when this is called.\nfunc (e *Engine) dispatchRestartNotify(req *RestartRequest) error {\n\tp := e.lookupReadyPlatform(req.Platform)\n\tif p == nil {\n\t\treturn fmt.Errorf(\"platform %q not ready\", req.Platform)\n\t}\n\trc, ok := p.(ReplyContextReconstructor)\n\tif !ok {\n\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}","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/engine.go#L273-L309","documentation":"This error wraps the failure of ReplyContextReconstructor.ReconstructReplyCtx during a restart notification. After the process restarts (e.g. via /restart + syscall.Exec), the engine must rebuild the platform-specific reply context (chat id, message id, thread info) from the stored session key so it can push a 'restart succeeded' message. If the platform adapter cannot reconstruct that context — usually because the referenced chat/session no longer exists, the stored key is stale after the restart, or the platform API call to look it up failed — dispatchRestartNotify aborts with 'reconstruct reply ctx: %w'.","triggerScenarios":"Engine.dispatchRestartNotify is called by runPendingRestartNotify after a restart; p.(ReplyContextReconstructor) succeeded but rc.ReconstructReplyCtx(req.SessionKey) returned an error (platform API lookup failed, session key refers to a deleted/archived chat, bot removed from the chat, or transient API/auth failure).","commonSituations":"User deleted the chat or removed the bot between invoking /restart and process re-exec; session keys persisted from an old workspace/config pointing at chats that no longer exist; platform token expired during the restart window; Feishu/Telegram API temporarily unreachable right after process restart.","solutions":["Inspect the wrapped cause (the %w inner error) in logs to see whether it is an API/auth failure or a not-found lookup; fix that underlying issue first.","Re-trigger the conversation: send any message in the original chat so the platform re-registers a fresh reply context, then retry /restart.","Verify the session key stored in the pending RestartRequest still points at an existing chat (not a deleted/archived channel or DM the bot left).","Check platform credentials (token expiry) — re-authenticate or refresh the bot token before restarting again.","If the platform API was transiently down, wait and retry /restart; the notify is not critical to the restart itself."],"exampleFix":"// before: reconstruct fails with stale session key after bot was removed\n// engine: rctx, err := rc.ReconstructReplyCtx(req.SessionKey)\n// after: guard the caller — only request restart notify for a live, reachable chat\nif rc, ok := p.(ReplyContextReconstructor); ok {\n    if _, err := rc.ReconstructReplyCtx(req.SessionKey); err != nil {\n        slog.Warn(\"skip restart notify, ctx unreachable\", \"err\", err)\n        return nil // proceed with restart without notification\n    }\n}","handlingStrategy":"try-catch","validationCode":"// Go: verify reconstructability before scheduling a restart notify\nif rc, ok := platform.(core.ReplyContextReconstructor); ok {\n    if _, err := rc.ReconstructReplyCtx(sessionKey); err != nil {\n        log.Printf(\"restart notify will fail for %s: %v\", sessionKey, err)\n    }\n}","typeGuard":"rc, ok := p.(core.ReplyContextReconstructor); if !ok { /* skip notify */ }","tryCatchPattern":"if err := engine.NotifyRestart(req); err != nil {\n    var ctxErr error\n    if errors.As(err, &ctxErr) && strings.Contains(err.Error(), \"reconstruct reply ctx\") {\n        slog.Warn(\"restart completed but notify failed; chat unreachable\", \"err\", err)\n    } else {\n        slog.Error(\"restart notify failed\", \"err\", err)\n    }\n}","preventionTips":["Send a message in the target chat shortly before /restart to confirm the reply context is live.","Treat restart-notify failure as non-fatal: the restart itself already succeeded.","Persist session keys only for chats the bot is still a member of; prune stale ones.","Monitor the wrapped inner error for auth/token expiry and refresh credentials proactively."],"tags":["restart","reply-context","platform-adapter","wrapped-error"],"backgroundTag":"resource-not-found","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}