{"record":{"id":"d297d56bfa2f8cd7","repo":"chenhg5/cc-connect","slug":"discord-send-button-followup-w","errorCode":null,"errorMessage":"discord: send button followup: %w","messagePattern":"discord: send button followup: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/discord/discord.go","lineNumber":1197,"sourceCode":"\tif len(buttons) == 0 {\n\t\treturn fmt.Errorf(\"discord: no buttons provided\")\n\t}\n\tcomponents := buildDiscordActionRows(buttons)\n\tif len(components) == 0 {\n\t\treturn fmt.Errorf(\"discord: no buttons provided\")\n\t}\n\n\tswitch rc := rctx.(type) {\n\tcase *interactionReplyCtx:\n\t\tif err := p.sendInteraction(rc, content); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err := p.session.FollowupMessageCreate(rc.interaction, true, &discordgo.WebhookParams{\n\t\t\tContent:    content,\n\t\t\tComponents: components,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"discord: send button followup: %w\", err)\n\t\t}\n\t\treturn nil\n\tcase replyContext:\n\t\t_, err := p.session.ChannelMessageSendComplex(rc.targetChannelID(), &discordgo.MessageSend{\n\t\t\tContent:    content,\n\t\t\tComponents: components,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"discord: send channel buttons: %w\", err)\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn core.ErrNotSupported\n\t}\n}\n\nfunc (p *progressPlatform) ProgressUpdateInterval() time.Duration {\n\treturn 2 * time.Second","sourceCodeStart":1179,"sourceCodeEnd":1215,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/discord/discord.go#L1179-L1215","documentation":"For interaction reply contexts, SendWithButtons first sends the interaction response, then posts the button message as a followup via FollowupMessageCreate; this error wraps any failure of that followup webhook call. The initial interaction reply succeeded, but the followup (which carries the buttons) failed. Root cause lives in the wrapped discordgo error.","triggerScenarios":"Interaction token expired (webhook tokens are valid 15 minutes after the interaction) or was already consumed; the bot lacks permission in the channel; Discord API returned 4xx/5xx to the webhook endpoint.","commonSituations":"Long-running agent work delayed the followup past the 15-minute interaction-token lifetime; bot kicked from channel between replies; transient network/API outage mid-conversation.","solutions":["Check the wrapped error: 404 unknown webhook means the interaction token expired — re-send as a normal channel message instead","Send the followup promptly after replying to the interaction, well within 15 minutes","Fall back to ChannelMessageSendComplex with components when the followup fails","Verify bot SEND_MESSAGES/VIEW_CHANNEL permissions in the target channel"],"exampleFix":"// before\n_, err := p.session.FollowupMessageCreate(rc.interaction, true, params)\n// after\n_, err := p.session.FollowupMessageCreate(rc.interaction, true, params)\nif err != nil {\n    _, err = p.session.ChannelMessageSendComplex(rc.channelID, &discordgo.MessageSend{Content: content, Components: components})\n}","handlingStrategy":"try-catch","validationCode":"if time.Since(rc.interactionCreatedAt) > 14*time.Minute {\n    return errors.New(\"interaction token near expiry; use channel send instead\")\n}","typeGuard":null,"tryCatchPattern":"err := p.SendWithButtons(ctx, rctx, content, buttons)\nif err != nil {\n    var restErr *discordgo.RESTError\n    if errors.As(err, &restErr) && restErr.Message.Code == 10060 /* unknown webhook */ {\n        // interaction token expired: fall back to channel message\n        return sendChannelFallback(ctx, content, buttons)\n    }\n    return err\n}","preventionTips":["Send followups promptly, well within the 15-minute interaction-token window","Implement a channel-message fallback for expired interaction tokens","Monitor 404 unknown-webhook errors as a token-expiry signal","Verify channel permissions for the bot in advance"],"tags":["discord","interaction","webhook","followup","api-error"],"backgroundTag":"api-request-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}