{"record":{"id":"073eeaa17b1779d8","repo":"bytebase/bytebase","slug":"failed-to-send-message-to-user-v","errorCode":null,"errorMessage":"failed to send message to user %v","messagePattern":"failed to send message to user (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/plugin/webhook/slack/slack.go","lineNumber":286,"sourceCode":"\t\t\terr := func() error {\n\t\t\t\tuserID, err := p.lookupByEmail(ctx, u.Email)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"failed to lookup user\")\n\t\t\t\t}\n\t\t\t\tif userID == \"\" {\n\t\t\t\t\treturn errors.Errorf(\"failed to find user id for %v\", u.Email)\n\t\t\t\t}\n\t\t\t\tchannelID, err := p.openConversation(ctx, userID)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"failed to open conversation\")\n\t\t\t\t}\n\t\t\t\tif err := p.chatPostMessage(ctx, channelID, webhookCtx); err != nil {\n\t\t\t\t\treturn errors.Wrapf(err, \"failed to post message\")\n\t\t\t\t}\n\t\t\t\tsent[u.Email] = true\n\t\t\t\treturn nil\n\t\t\t}()\n\t\t\tmultierr.AppendInto(&errs, errors.Wrapf(err, \"failed to send message to user %v\", u.Email))\n\t\t}\n\t\treturn errs\n\t}); err != nil {\n\t\tslog.Warn(\"failed to send direct message to slack user\", log.BBError(err), slog.String(\"event\", webhookCtx.EventType))\n\t\treturn false\n\t}\n\n\treturn true\n}\n","sourceCodeStart":268,"sourceCodeEnd":296,"githubUrl":"https://github.com/bytebase/bytebase/blob/1870550677fe08f0d2a78c07acd27541464eb945/backend/plugin/webhook/slack/slack.go#L268-L296","documentation":"This is a wrapping error produced in the Slack webhook plugin's Post() when the goroutine that sends a per-user direct message fails for a given user u. The errlist package collects each per-user failure via multierr.AppendInto, so one bad recipient does not stop the others; the combined error is then logged with slog.Warn and the function returns false (indicating the notification was not delivered to all users). It means at least one Slack DM (chat.postMessage) failed for that specific user's email.","triggerScenarios":"Post() iterates over Slack users resolved from the instance URL and calls chatPostMessage(ctx, channelID, webhookCtx) for each; any failure of that API call (invalid channel ID, Slack API error, expired bot token, network failure) is wrapped as 'failed to post message' then re-wrapped as 'failed to send message to user %v' with the user's email.","commonSituations":"Bot removed from the workspace or channel; user has DMs disabled or the bot is restricted from messaging them; invalid/stale Slack bot token after reinstallation; Slack API rate limiting (429) or transient network outage during bulk sends.","solutions":["Check the server logs for the accompanying 'failed to post message' cause and the Slack error code in the wrapped error","Verify the Slack bot token is valid and the bot is a member of the target channel/user's DM scope","Confirm the user's Slack identity mapping (email) still exists in the workspace","Retry; if rate-limited (429), respect Slack's Retry-After or reduce notification fan-out"],"exampleFix":"// before\nif err := p.chatPostMessage(ctx, channelID, webhookCtx); err != nil {\n\treturn errors.Wrapf(err, \"failed to post message\")\n}\n// after\nif err := p.chatPostMessage(ctx, channelID, webhookCtx); err != nil {\n\tvar slackErr *slack.SlackErrorResponse\n\tif errors.As(err, &slackErr) && slackErr.Err == \"ratelimited\" {\n\t\ttime.Sleep(retryAfter) // honor Retry-After before failing\n\t\treturn errors.Wrapf(err, \"failed to post message\")\n\t}\n\treturn errors.Wrapf(err, \"failed to post message\")\n}","handlingStrategy":"try-catch","validationCode":"// Verify Slack API reachability and token before sending\n// resp, err := slackClient.AuthTest(); err == nil && resp.OK","typeGuard":"func hasSlackUser(u *User) bool { return u != nil && u.Email != \"\" }","tryCatchPattern":"// Go: multierr already isolates per-user failures; inspect the joined error\nif err := p.Post(ctx, webhookCtx); err != nil {\n\tslog.Warn(\"slack dm failed\", log.BBError(err)) // parse per-user segments from err\n}","preventionTips":["Keep the Slack bot installed and token valid; re-auth after app reinstall","Invite the bot to the channel and confirm it may DM workspace members","Monitor 429 rate limits when fanning out to many users","Keep user email ↔ Slack identity mappings current"],"tags":["slack","webhook","notification","multierr"],"backgroundTag":"http-request-failed","analyzedSha":"1870550677fe08f0d2a78c07acd27541464eb945","analyzedAt":"2026-09-06T21:16:13.665Z","contentChangedAt":"2026-09-06T21:16:13.665Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}