chenhg5/cc-connect · warning

reconstruct reply ctx: %w

Error message

reconstruct reply ctx: %w

What it means

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'.

Source

Thrown at core/engine.go:291

	}
	e.pendingRestartMu.Unlock()
}

// dispatchRestartNotify sends the notify to the target platform with up
// to 3 attempts (initial + 2 retries) on transient failure. The
// platform must already be ready when this is called.
func (e *Engine) dispatchRestartNotify(req *RestartRequest) error {
	p := e.lookupReadyPlatform(req.Platform)
	if p == nil {
		return fmt.Errorf("platform %q not ready", req.Platform)
	}
	rc, ok := p.(ReplyContextReconstructor)
	if !ok {
		return fmt.Errorf("platform %q does not support ReconstructReplyCtx", req.Platform)
	}
	rctx, err := rc.ReconstructReplyCtx(req.SessionKey)
	if err != nil {
		return fmt.Errorf("reconstruct reply ctx: %w", err)
	}
	text := e.i18n.T(MsgRestartSuccess)
	if CurrentVersion != "" {
		text += fmt.Sprintf(" (%s)", CurrentVersion)
	}

	backoffs := []time.Duration{0, 500 * time.Millisecond, 1500 * time.Millisecond}
	var lastErr error
	for attempt, wait := range backoffs {
		if wait > 0 {
			time.Sleep(wait)
		}
		if err := e.waitOutgoing(p); err != nil {
			lastErr = fmt.Errorf("wait outgoing: %w", err)
			slog.Warn("restart notify: wait outgoing failed",
				"platform", req.Platform, "attempt", attempt+1, "error", err)
			continue
		}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. 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.
  2. Re-trigger the conversation: send any message in the original chat so the platform re-registers a fresh reply context, then retry /restart.
  3. 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).
  4. Check platform credentials (token expiry) — re-authenticate or refresh the bot token before restarting again.
  5. If the platform API was transiently down, wait and retry /restart; the notify is not critical to the restart itself.

Example fix

// before: reconstruct fails with stale session key after bot was removed
// engine: rctx, err := rc.ReconstructReplyCtx(req.SessionKey)
// after: guard the caller — only request restart notify for a live, reachable chat
if rc, ok := p.(ReplyContextReconstructor); ok {
    if _, err := rc.ReconstructReplyCtx(req.SessionKey); err != nil {
        slog.Warn("skip restart notify, ctx unreachable", "err", err)
        return nil // proceed with restart without notification
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: verify reconstructability before scheduling a restart notify
if rc, ok := platform.(core.ReplyContextReconstructor); ok {
    if _, err := rc.ReconstructReplyCtx(sessionKey); err != nil {
        log.Printf("restart notify will fail for %s: %v", sessionKey, err)
    }
}

Type guard

rc, ok := p.(core.ReplyContextReconstructor); if !ok { /* skip notify */ }

Try / catch

if err := engine.NotifyRestart(req); err != nil {
    var ctxErr error
    if errors.As(err, &ctxErr) && strings.Contains(err.Error(), "reconstruct reply ctx") {
        slog.Warn("restart completed but notify failed; chat unreachable", "err", err)
    } else {
        slog.Error("restart notify failed", "err", err)
    }
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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