chenhg5/cc-connect · error

discord: invalid preview handle type %T

Error message

discord: invalid preview handle type %T

What it means

UpdateMessage requires the previewHandle argument to be the *discordPreviewHandle previously returned by SendPreviewStart; any other value fails this check before editing. This guards against editing with a handle from another platform or a stale/incorrect value. The %T output identifies the actual type received.

Source

Thrown at platform/discord/discord.go:1279

	case *interactionReplyCtx:
		channelID = rc.channelID
	default:
		return nil, fmt.Errorf("discord: invalid reply context type %T", rctx)
	}

	msg := buildDiscordPreviewMessage(content)
	sent, err := p.session.ChannelMessageSendComplex(channelID, msg)
	if err != nil {
		return nil, fmt.Errorf("discord: send preview: %w", err)
	}
	return &discordPreviewHandle{channelID: channelID, messageID: sent.ID}, nil
}

// UpdateMessage edits an existing message identified by previewHandle.
func (p *Platform) UpdateMessage(ctx context.Context, previewHandle any, content string) error {
	h, ok := previewHandle.(*discordPreviewHandle)
	if !ok {
		return fmt.Errorf("discord: invalid preview handle type %T", previewHandle)
	}
	_, err := p.session.ChannelMessageEditComplex(buildDiscordPreviewEdit(h.channelID, h.messageID, content))
	if err != nil {
		return fmt.Errorf("discord: edit message: %w", err)
	}
	return nil
}

// DeletePreviewMessage removes the preview message so the final response can
// be sent as a fresh message (avoids notification confusion).
func (p *Platform) DeletePreviewMessage(ctx context.Context, previewHandle any) error {
	h, ok := previewHandle.(*discordPreviewHandle)
	if !ok {
		return fmt.Errorf("discord: invalid preview handle type %T", previewHandle)
	}
	return p.session.ChannelMessageDelete(h.channelID, h.messageID)
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Always thread through the exact *discordPreviewHandle returned by SendPreviewStart
  2. Check the %T in the error to find which code path produced the wrong handle type
  3. Recreate the preview via SendPreviewStart if the handle was lost; handles are not reconstructible from IDs alone in this API
  4. Type-assert at the call site and handle failure gracefully before calling UpdateMessage

Example fix

// before
p.UpdateMessage(ctx, messageIDString, content)
// after
handle, err := p.SendPreviewStart(ctx, rctx, content)
if err != nil { return err }
return p.UpdateMessage(ctx, handle, content)
Defensive patterns

Strategy: type-guard

Type guard

func isValidPreviewHandle(h any) bool {
    _, ok := h.(*discordPreviewHandle)
    return ok
}

Try / catch

if err := p.UpdateMessage(ctx, previewHandle, content); err != nil {
    if strings.Contains(err.Error(), "invalid preview handle type") {
        log.Printf("bad handle %T; recreating preview", previewHandle)
        return recreatePreview(ctx, content)
    }
    return err
}

Prevention

When it happens

Trigger: UpdateMessage called with a handle obtained from a different platform's preview implementation, a raw message-ID string, or nil instead of the handle returned by SendPreviewStart.

Common situations: Mixing preview implementations across platform adapters; persisting handles in a lossy format (e.g. JSON without type) and re-decoding into the wrong type; ignoring SendPreviewStart's return value.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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