{"record":{"id":"ac2a5035146b0705","repo":"mattermost-community/focalboard","slug":"cannot-get-direct-channel-w-ac2a50","errorCode":null,"errorMessage":"cannot get direct channel: %w","messagePattern":"cannot get direct channel: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/services/notify/plugindelivery/subscription_deliver.go","lineNumber":57,"sourceCode":"\t\tChannelId: channelID,\n\t}\n\n\tmm_model.ParseSlackAttachment(post, attachments)\n\n\t_, err = pd.api.CreatePost(post)\n\treturn err\n}\n\nfunc (pd *PluginDelivery) getDirectChannelID(teamID string, subscriberID string, subscriberType model.SubscriberType, botID string) (string, error) {\n\tswitch subscriberType {\n\tcase model.SubTypeUser:\n\t\tuser, err := pd.api.GetUserByID(subscriberID)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"cannot find user: %w\", err)\n\t\t}\n\t\tchannel, err := pd.getDirectChannel(teamID, user.Id, botID)\n\t\tif err != nil || channel == nil {\n\t\t\treturn \"\", fmt.Errorf(\"cannot get direct channel: %w\", err)\n\t\t}\n\t\treturn channel.Id, nil\n\tcase model.SubTypeChannel:\n\t\treturn subscriberID, nil\n\tdefault:\n\t\treturn \"\", ErrUnsupportedSubscriberType\n\t}\n}\n\nfunc (pd *PluginDelivery) getDirectChannel(teamID string, userID string, botID string) (*mm_model.Channel, error) {\n\t// first ensure the bot is a member of the team.\n\t_, err := pd.api.CreateMember(teamID, botID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cannot add bot to team %s: %w\", teamID, err)\n\t}\n\treturn pd.api.GetDirectChannelOrCreate(userID, botID)\n}\n","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/mattermost-community/focalboard/blob/a84bbb65e32edf972856b329417096ac413518e9/server/services/notify/plugindelivery/subscription_deliver.go#L39-L75","documentation":"getDirectChannelID wraps the failure of pd.getDirectChannel when resolving the direct-message channel between a subscriber and the delivery bot. Note the bug-prone shape: if getDirectChannel returns (nil, nil) — a nil channel with no error — the %w wraps a nil err, producing an error with a trailing empty cause. It means either the bot could not be added to the team or the direct channel could not be found/created in Mattermost.","triggerScenarios":"SubscriptionDeliverSlackAttachments resolves a subscriber of type DirectMessage; pd.api.CreateMember(teamID, botID) fails, or pd.api.GetDirectChannelOrCreate(userID, botID) fails, or returns nil channel with nil error.","commonSituations":"Bot user is deleted or deactivated; bot lacks permission to join the team; subscriber ID refers to a stale/deleted user (though that fails earlier with 'cannot find user'); Mattermost API connectivity/permission problems in the plugin.","solutions":["Verify the bot account exists, is active, and has permission to be added as a team member.","Check the subscriber record points to a valid Mattermost user ID in the target team.","Inspect the wrapped cause (%w) from the Mattermost API logs to see whether CreateMember or GetDirectChannelOrCreate failed.","Fix the nil-channel nil-error case by returning an explicit error when channel == nil instead of wrapping a nil err."],"exampleFix":"// before\nchannel, err := pd.getDirectChannel(teamID, user.Id, botID)\nif err != nil || channel == nil {\n\treturn \"\", fmt.Errorf(\"cannot get direct channel: %w\", err)\n}\n// after\nchannel, err := pd.getDirectChannel(teamID, user.Id, botID)\nif err != nil {\n\treturn \"\", fmt.Errorf(\"cannot get direct channel: %w\", err)\n}\nif channel == nil {\n\treturn \"\", fmt.Errorf(\"cannot get direct channel for user %s: channel is nil\", user.Id)\n}","handlingStrategy":"try-catch","validationCode":"// before delivering, verify subscriber user exists (plugin API)\nu, err := api.GetUserByID(subscriberID)\nif err != nil || u == nil || u.DeleteAt != 0 {\n\t// skip/queue this subscriber instead of attempting delivery\n}","typeGuard":"func validChannel(c *mm_model.Channel) bool { return c != nil && c.Id != \"\" }","tryCatchPattern":"chID, err := getDirectChannelID(teamID, subscriberID, botID)\nif err != nil {\n\tvar nilCh *NilChannelError\n\tif errors.As(err, &nilCh) || strings.Contains(err.Error(), \"cannot get direct channel\") {\n\t\tlogger.Warn(\"skipping DM subscriber\", \"subscriber\", subscriberID, \"cause\", err)\n\t\treturn // degrade gracefully, don't fail whole delivery\n\t}\n\treturn err\n}","preventionTips":["Keep the bot active and a member of all teams with subscriptions.","Treat nil-channel/nil-error explicitly instead of wrapping a nil err.","Monitor Mattermost API errors in plugin logs for CreateMember/GetDirectChannelOrCreate."],"tags":["mattermost","plugin","direct-channel","error-wrapping"],"backgroundTag":"direct-channel-resolution-failed","analyzedSha":"a84bbb65e32edf972856b329417096ac413518e9","analyzedAt":"2026-08-30T09:22:20.720Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}