chenhg5/cc-connect · error

discord: invalid reply context type %T

Error message

discord: invalid reply context type %T

What it means

Reply() received a reply-context value that is neither the platform's internal *interactionReplyCtx nor its replyContext struct. The Discord platform creates these contexts itself when a message arrives, so hitting the default branch means a foreign or zero-value context was passed in — typically a reply context captured from a different platform and reused on this one.

Source

Thrown at platform/discord/discord.go:960

	if action, found := strings.CutPrefix(customID, "perm:"); found {
		switch action {
		case "allow", "deny":
			return action, true, true
		case "allow_all":
			return "allow all", true, true
		}
	}
	return "", false, false
}

func (p *Platform) Reply(ctx context.Context, rctx any, content string) error {
	switch rc := rctx.(type) {
	case *interactionReplyCtx:
		return p.sendInteraction(rc, content)
	case replyContext:
		return p.sendChannelReply(rc, content)
	default:
		return fmt.Errorf("discord: invalid reply context type %T", rctx)
	}
}

// Send sends a new message (not a reply).
func (p *Platform) Send(ctx context.Context, rctx any, content string) error {
	switch rc := rctx.(type) {
	case *interactionReplyCtx:
		return p.sendInteraction(rc, content)
	case replyContext:
		return p.sendChannel(rc, content)
	default:
		return fmt.Errorf("discord: invalid reply context type %T", rctx)
	}
}

// sendInteraction delivers a message through the Discord interaction response
// mechanism. The first call edits the deferred "thinking" response; subsequent
// calls create followup messages.

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Ensure Reply is only called with the ReplyCtx that the Discord platform itself attached to core.Message — never reuse contexts across platforms
  2. Check that the originating message actually came from the Discord platform before calling p.Reply
  3. Guard the call site with a nil/origin check on rctx before invoking Reply
  4. If bridging, capture the content and send via Send with a Discord-native context instead of replaying a foreign one

Example fix

// before
err := discordPlatform.Reply(ctx, msg.ReplyCtx, out)
// after
if rc, ok := msg.ReplyCtx.(discord.ReplyContext); ok {
    err = discordPlatform.Reply(ctx, rc, out)
}
Defensive patterns

Strategy: type-guard

Validate before calling

if rctx == nil {
    return fmt.Errorf("no reply context: message did not originate from discord")
}

Type guard

func isDiscordReplyCtx(rctx any) bool {
    switch rctx.(type) {
    case *discordinteractionReplyCtx, discordreplyContext:
        return true
    default:
        return false
    }
}

Try / catch

if err := p.Reply(ctx, rctx, content); err != nil {
    if strings.Contains(err.Error(), "invalid reply context type") {
        slog.Warn("non-discord reply context; dropping reply")
        return nil
    }
    return err
}

Prevention

When it happens

Trigger: Passing nil or a ReplyCtx produced by another platform (e.g. a Feishu or Telegram reply context) into discord.Platform.Reply; storing a ReplyCtx across platforms and replaying it; calling Reply with an empty any value from custom engine code.

Common situations: Bridges or forwarding logic that routes messages between platforms and copies ReplyCtx without checking which platform produced it; tests or scripts that hand-craft reply contexts; a nil ReplyCtx after an engine bug fails to attach one.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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