chenhg5/cc-connect · error

bridge: invalid preview handle

Error message

bridge: invalid preview handle

What it means

DeletePreviewMessage requires the previewHandle to be the concrete *bridgeReplyCtx type that RequestPreviewMessage returned. This error is thrown when the handle is nil or of any other type, because the bridge cannot recover the platform and reply coordinates needed to route a delete_message command.

Source

Thrown at core/bridge.go:574

	case handle := <-ch:
		return newBridgeReplyCtx(a, rc.SessionKey, handle), nil
	case <-time.After(10 * time.Second):
		a.previewMu.Lock()
		delete(a.previewRequests, refID)
		a.previewMu.Unlock()
		return nil, fmt.Errorf("bridge: preview_ack timeout")
	case <-ctx.Done():
		a.previewMu.Lock()
		delete(a.previewRequests, refID)
		a.previewMu.Unlock()
		return nil, ctx.Err()
	}
}

func (bp *BridgePlatform) DeletePreviewMessage(ctx context.Context, previewHandle any) error {
	rc, ok := previewHandle.(*bridgeReplyCtx)
	if !ok {
		return fmt.Errorf("bridge: invalid preview handle")
	}
	a := bp.server.getAdapter(rc.Platform)
	if a == nil || !a.capabilities["delete_message"] {
		return ErrNotSupported
	}
	return bp.server.sendToAdapter(rc.Platform, map[string]any{
		"type":           "delete_message",
		"session_key":    rc.SessionKey,
		"preview_handle": rc.ReplyCtx,
	})
}

func (bp *BridgePlatform) StartTyping(ctx context.Context, replyCtx any) (stop func()) {
	rc, ok := replyCtx.(*bridgeReplyCtx)
	if !ok {
		return func() {}
	}
	a := bp.server.getAdapter(rc.Platform)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Use the exact *bridgeReplyCtx value returned by RequestPreviewMessage — never re-serialize or copy it into another type.
  2. Check the handle is non-nil and comes from the same BridgePlatform/server instance that created it.
  3. If handles must be persisted, persist an identifier and re-request the preview rather than reconstructing the handle object.
  4. Guard the call with a type assertion and skip/log when the assertion fails instead of passing a bad handle through.

Example fix

// before
var h any = storedPreviewHandleFromJSON
err := p.DeletePreviewMessage(ctx, h)
// after
rc, ok := storedPreviewHandle.(*core.BridgeReplyCtx) // keep original pointer type
if !ok { return fmt.Errorf("no valid preview handle") }
err := p.DeletePreviewMessage(ctx, rc)
Defensive patterns

Strategy: type-guard

Validate before calling

func canDeletePreview(h any) bool {
    _, ok := h.(*core.BridgeReplyCtx)
    return ok
}

Type guard

func asPreviewHandle(h any) (*core.BridgeReplyCtx, bool) {
    rc, ok := h.(*core.BridgeReplyCtx)
    return rc, ok
}

Try / catch

rc, ok := handle.(*core.BridgeReplyCtx)
if !ok {
    return fmt.Errorf("preview handle unavailable (type %T)", handle)
}
if err := p.DeletePreviewMessage(ctx, rc); err != nil {
    slog.Warn("delete preview failed", "err", err)
}

Prevention

When it happens

Trigger: Calling BridgePlatform.DeletePreviewMessage with nil, with a handle obtained from a different platform adapter, or with a deserialized/reconstructed map[string]any instead of the *bridgeReplyCtx pointer issued by RequestPreviewMessage.

Common situations: Storing handles in JSON (e.g. persisting state to disk or a DB) and passing the decoded generic value back; passing a handle from another Platform implementation; passing nil after a failed preview request.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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