{"record":{"id":"9548e3cac7f60211","repo":"chenhg5/cc-connect","slug":"resolve-parent-channel-s-w","errorCode":null,"errorMessage":"resolve parent channel %s: %w","messagePattern":"resolve parent channel (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/discord/discord.go","lineNumber":442,"sourceCode":"func resolveCronReplyTarget(sessionKey, title string, ops discordThreadOps) (string, replyContext, error) {\n\tchannelID, err := parseDiscordSessionKeyChannelID(sessionKey)\n\tif err != nil {\n\t\treturn \"\", replyContext{}, err\n\t}\n\n\tch, err := ops.ResolveChannel(channelID)\n\tif err != nil {\n\t\treturn \"\", replyContext{}, fmt.Errorf(\"resolve channel %s: %w\", channelID, err)\n\t}\n\tparentChannelID := channelID\n\tparentType := ch.Type\n\tif isThreadChannelType(ch.Type) {\n\t\tif ch.ParentID == \"\" {\n\t\t\treturn \"\", replyContext{}, core.ErrNotSupported\n\t\t}\n\t\tparent, err := ops.ResolveChannel(ch.ParentID)\n\t\tif err != nil {\n\t\t\treturn \"\", replyContext{}, fmt.Errorf(\"resolve parent channel %s: %w\", ch.ParentID, err)\n\t\t}\n\t\tparentChannelID = ch.ParentID\n\t\tparentType = parent.Type\n\t}\n\n\tthreadType, ok := standaloneThreadType(parentType)\n\tif !ok {\n\t\treturn \"\", replyContext{}, core.ErrNotSupported\n\t}\n\n\tthread, err := ops.StartStandaloneThread(parentChannelID, freshThreadName(title), threadType, 1440)\n\tif err != nil {\n\t\treturn \"\", replyContext{}, fmt.Errorf(\"start thread in channel %s: %w\", parentChannelID, err)\n\t}\n\tif err := ops.JoinThread(thread.ID); err != nil {\n\t\tslog.Debug(\"discord: join fresh thread failed\", \"thread\", thread.ID, \"error\", err)\n\t}\n","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/discord/discord.go#L424-L460","documentation":"When the cron target channel is itself a thread, resolveCronReplyTarget must resolve the parent channel (ch.ParentID) to know what kind of standalone thread to create. Failure to resolve the parent is wrapped as \"resolve parent channel <id>: <cause>\". An empty ParentID returns core.ErrNotSupported separately.","triggerScenarios":"ops.ResolveChannel(ch.ParentID) fails for the thread's parent — parent channel deleted, bot lacks access to the parent (but can see the thread), or a transient Discord API error.","commonSituations":"Threads whose parent channel was deleted (orphaned threads), permission asymmetry where the bot sees a thread but not its parent category/channel, Discord API hiccups during cron firing.","solutions":["Inspect the wrapped cause: not-found usually means the parent channel was deleted.","Re-point the cron job at a normal text channel instead of a thread of a deleted channel.","Grant the bot ViewChannel on the parent channel/category, not just the thread.","Retry on transient API errors with backoff."],"exampleFix":"// before\nparent, err := ops.ResolveChannel(ch.ParentID)\nif err != nil {\n    return \"\", replyContext{}, fmt.Errorf(\"resolve parent channel %s: %w\", ch.ParentID, err)\n}\n// after\nparent, err := ops.ResolveChannel(ch.ParentID)\nif err != nil {\n    slog.Warn(\"discord: cannot resolve thread parent for cron, using thread channel directly\", \"parent\", ch.ParentID, \"error\", err)\n    parentChannelID = channelID\n    parentType = ch.Type\n}","handlingStrategy":"fallback","validationCode":"if ch.ParentID == \"\" {\n    return core.ErrNotSupported // check before resolving parent\n}\nif _, err := ops.ResolveChannel(ch.ParentID); err != nil {\n    slog.Warn(\"discord: thread parent unresolvable for cron\", \"parent\", ch.ParentID)\n}","typeGuard":null,"tryCatchPattern":"parent, err := ops.ResolveChannel(ch.ParentID)\nif err != nil {\n    // degrade: create the standalone thread in the thread's own channel scope or skip\n    return \"\", replyContext{}, fmt.Errorf(\"resolve parent channel %s: %w\", ch.ParentID, err)\n}","preventionTips":["Grant the bot visibility on parent channels/categories, not just threads.","Clean up cron jobs whose target threads' parents have been deleted.","Prefer scheduling crons on plain text channels to avoid parent resolution entirely."],"tags":["discord","cron","thread","parent-channel"],"backgroundTag":"resource-not-found","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"}