bytebase/bytebase · warning

failed to send message to user %v

Error message

failed to send message to user %v

What it means

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.

Source

Thrown at backend/plugin/webhook/slack/slack.go:286

			err := func() error {
				userID, err := p.lookupByEmail(ctx, u.Email)
				if err != nil {
					return errors.Wrapf(err, "failed to lookup user")
				}
				if userID == "" {
					return errors.Errorf("failed to find user id for %v", u.Email)
				}
				channelID, err := p.openConversation(ctx, userID)
				if err != nil {
					return errors.Wrapf(err, "failed to open conversation")
				}
				if err := p.chatPostMessage(ctx, channelID, webhookCtx); err != nil {
					return errors.Wrapf(err, "failed to post message")
				}
				sent[u.Email] = true
				return nil
			}()
			multierr.AppendInto(&errs, errors.Wrapf(err, "failed to send message to user %v", u.Email))
		}
		return errs
	}); err != nil {
		slog.Warn("failed to send direct message to slack user", log.BBError(err), slog.String("event", webhookCtx.EventType))
		return false
	}

	return true
}

View on GitHub (pinned to 1870550677)

Solutions

  1. Check the server logs for the accompanying 'failed to post message' cause and the Slack error code in the wrapped error
  2. Verify the Slack bot token is valid and the bot is a member of the target channel/user's DM scope
  3. Confirm the user's Slack identity mapping (email) still exists in the workspace
  4. Retry; if rate-limited (429), respect Slack's Retry-After or reduce notification fan-out

Example fix

// before
if err := p.chatPostMessage(ctx, channelID, webhookCtx); err != nil {
	return errors.Wrapf(err, "failed to post message")
}
// after
if err := p.chatPostMessage(ctx, channelID, webhookCtx); err != nil {
	var slackErr *slack.SlackErrorResponse
	if errors.As(err, &slackErr) && slackErr.Err == "ratelimited" {
		time.Sleep(retryAfter) // honor Retry-After before failing
		return errors.Wrapf(err, "failed to post message")
	}
	return errors.Wrapf(err, "failed to post message")
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Verify Slack API reachability and token before sending
// resp, err := slackClient.AuthTest(); err == nil && resp.OK

Type guard

func hasSlackUser(u *User) bool { return u != nil && u.Email != "" }

Try / catch

// Go: multierr already isolates per-user failures; inspect the joined error
if err := p.Post(ctx, webhookCtx); err != nil {
	slog.Warn("slack dm failed", log.BBError(err)) // parse per-user segments from err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06). Data as JSON: /api/errors/073eeaa17b1779d8. Report an issue: GitHub.