ory/kratos · error
unable to dispatch mail delivery because upstream server…
Error message
unable to dispatch mail delivery because upstream server replied with status code %d
What it means
The HTTP courier channel finished POSTing a message to the configured upstream URL and received a response with a status outside the 2xx range. The upstream delivery endpoint rejected the request; the status code is included in the error.
Solutions
- Check the upstream server's logs for why it returned the non-success status
- Verify the request URL, auth and payload format expected by the HTTP channel configuration
- If the failure was transient (e.g. 502/503), let the courier retry the dispatch
Defensive patterns
Strategy: retry
When it happens
Trigger: Thrown at courier/http_channel.go:121 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07).
Data as JSON: /api/errors/8a4c54dfa8e05463.
Report an issue: GitHub.
Appendix: source
Thrown at courier/http_channel.go:121
}
// Close the original body, not the NopCloser below: closing it returns
// the connection to the pool and ends the otelhttp client span.
defer func(body io.ReadCloser) { _ = body.Close() }(res.Body)
res.Body = io.NopCloser(io.LimitReader(res.Body, 1024))
logger := c.d.Logger().
WithField("http_server", c.requestConfig.URL).
WithField("message_id", msg.ID).
WithField("message_nid", msg.NID).
WithField("message_type", msg.Type).
WithField("message_template_type", msg.TemplateType).
WithField("message_subject", msg.Subject)
if res.StatusCode >= 200 && res.StatusCode < 300 {
logger.Debug("Courier sent out mailer.")
return nil
}
err = errors.Errorf(
"unable to dispatch mail delivery because upstream server replied with status code %d",
res.StatusCode,
)
body, _ := io.ReadAll(res.Body)
logger.
WithError(err).
WithField("http_response_body", string(body)).
Error("sending mail via HTTP failed.")
return errors.WithStack(err)
}
func (c *httpChannel) tryPopulateHTMLBody(ctx context.Context, tmpl Template, td *httpDataModel) {
if emailTmpl, ok := tmpl.(EmailTemplate); ok {
// Only get the HTML body from the template; plaintext body comes from msg.Body
// to maintain backward compatibility with existing behaviorView on GitHub (pinned to b86338da04)