chenhg5/cc-connect · error
start thread for message %s: %w
Error message
start thread for message %s: %w
What it means
For a guild message that is not already inside a thread, resolveThreadReplyContext starts a new thread on the message via ops.StartThread. If that Discord API call fails, the error is wrapped as "start thread for message <messageID>: <cause>" and message dispatch is aborted. Thread creation can fail for permissions, archived/locked parent threads, or API errors.
Source
Thrown at platform/discord/discord.go:415
threadID := m.Message.Thread.ID
if err := ops.JoinThread(threadID); err != nil {
slog.Debug("discord: join attached thread failed", "thread", threadID, "error", err)
}
rc := replyContext{channelID: threadID, messageID: m.ID, threadID: threadID}
return buildThreadSessionKey(threadID), rc, m.ChannelID, nil
}
if m.Flags&discordgo.MessageFlagsHasThread != 0 {
threadID := m.ID
if err := ops.JoinThread(threadID); err != nil {
slog.Debug("discord: join message thread failed", "thread", threadID, "error", err)
}
rc := replyContext{channelID: threadID, messageID: m.ID, threadID: threadID}
return buildThreadSessionKey(threadID), rc, m.ChannelID, nil
}
thread, err := ops.StartThread(m.ChannelID, m.ID, threadNameForMessage(m, botID), 1440)
if err != nil {
return "", replyContext{}, "", fmt.Errorf("start thread for message %s: %w", m.ID, err)
}
if err := ops.JoinThread(thread.ID); err != nil {
slog.Debug("discord: join new thread failed", "thread", thread.ID, "error", err)
}
rc := replyContext{channelID: thread.ID, messageID: m.ID, threadID: thread.ID}
return buildThreadSessionKey(thread.ID), rc, m.ChannelID, nil
}
func resolveCronReplyTarget(sessionKey, title string, ops discordThreadOps) (string, replyContext, error) {
channelID, err := parseDiscordSessionKeyChannelID(sessionKey)
if err != nil {
return "", replyContext{}, err
}
ch, err := ops.ResolveChannel(channelID)
if err != nil {
return "", replyContext{}, fmt.Errorf("resolve channel %s: %w", channelID, err)
}View on GitHub (pinned to 4000b2338a)
Solutions
- Read the wrapped cause; grant the bot "Create Public Threads" and "Send Messages in Threads" permissions.
- Confirm the message is <48h old and in a guild text channel eligible for threading.
- Unarchive/unlock the parent thread or post to the channel directly instead of threading.
- Retry on 429 with backoff, or fall back to replying in the parent channel when thread creation fails.
Example fix
// before
thread, err := ops.StartThread(m.ChannelID, m.ID, threadNameForMessage(m, botID), 1440)
if err != nil {
return "", replyContext{}, "", fmt.Errorf("start thread for message %s: %w", m.ID, err)
}
// after
thread, err := ops.StartThread(m.ChannelID, m.ID, threadNameForMessage(m, botID), 1440)
if err != nil {
slog.Warn("discord: thread start failed, falling back to channel", "message", m.ID, "error", err)
rc := replyContext{channelID: m.ChannelID, messageID: m.ID}
return buildThreadSessionKey(m.ChannelID), rc, m.ChannelID, nil
} Defensive patterns
Strategy: fallback
Validate before calling
// preflight: bot can create threads in this channel? perm, err := session.State.UserChannelPermissions(botID, m.ChannelID) canThread := err == nil && perm&discordgo.PermissionCreatePublicThreads != 0
Try / catch
thread, err := ops.StartThread(m.ChannelID, m.ID, name, 1440)
if err != nil {
slog.Warn("discord: falling back to channel reply", "message", m.ID, "error", err)
return buildThreadSessionKey(m.ChannelID), replyContext{channelID: m.ChannelID, messageID: m.ID}, m.ChannelID, nil
} Prevention
- Grant the bot Create Public Threads and Send Messages in Threads permissions.
- Only attempt threading on messages younger than 48 hours in guild text channels.
- Design replies to degrade gracefully to the parent channel when threading fails.
When it happens
Trigger: ops.StartThread(m.ChannelID, m.ID, name, 1440) fails: bot lacks "Create Public Threads" permission, the message is older than 48 hours, the channel is not a guild text channel, the channel/thread is archived or locked, or Discord returns 429/5xx.
Common situations: Bots added with default permissions lacking thread management rights, messages arriving in announcement/forum channels where thread start rules differ, long-running cron sessions trying to thread an old message, rate limits during high traffic.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- start thread in channel %s: %w
- discord: send fallback: %w
- discord: send: %w
- discord: send image fallback: %w
- discord: send image: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/94731b0a2a312ee4.
Report an issue: GitHub.