ory/kratos · error
no courier channels configured for
Error message
no courier channels configured for: %s
What it means
When DispatchMessage needs a channel, the channels method iterates configured courier channels looking for a matching ID. If none matches, it returns this error naming the requested channel ID. It means the message references a channel that is not present in the courier configuration.
Solutions
- Add the missing channel with the referenced ID to the courier.channels config.
- Compare the message's channel ID in the courier_messages table with the IDs in your config.
- Ensure the same courier config is used across environments or migrate queued messages accordingly.
- Delete or reassign stale queued messages referencing removed channel IDs.
Example fix
// before (config.yaml)
courier:
channels: []
// after
courier:
channels:
- id: sms
type: http
request_config:
method: POST
url: https://sms-provider.example/send Defensive patterns
Strategy: validation
Validate before calling
configured := map[string]bool{}
for _, ch := range cfg.Courier.Channels { configured[ch.ID] = true }
if !configured[messageChannelID] {
return fmt.Errorf("channel %q is not configured in courier.channels", messageChannelID)
} Type guard
null
Try / catch
if err := courier.DispatchMessage(ctx, msg); err != nil {
if strings.Contains(err.Error(), "no courier channels configured") {
log.Printf("message %s references unconfigured channel: %v", msg.ID, err)
// requeue, dead-letter, or fix config
return nil
}
return err
} Prevention
- Keep courier channel IDs stable across config changes and environments
- Search the courier_messages table for channel references before removing a channel
- Sync courier config between staging and production before deploying
When it happens
Trigger: Dispatching a message whose channel/recipient configuration points to a channel ID that does not appear in the courier channels list in config (e.g. message stored for channel "sms_provider" while config only defines "sms").
Common situations: Renaming or removing a channel in config while old messages still reference the old ID, running with a minimal config that omits channels while templates specify them, or mismatched IDs between environments (staging vs prod config).
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- unknown courier channel type
- expected to get the DSN as an argument, or the…
- required config value "dsn" was not set
- you must provide `secrets.pagination` for FIPS compliance
- you must provide `secrets.cipher` for FIPS compliance
AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07).
Data as JSON: /api/errors/da5212f1abfaf714.
Report an issue: GitHub.
Appendix: source
Thrown at courier/courier_dispatcher.go:43
for _, channel := range cs {
if channel.ID != id {
continue
}
switch channel.Type {
case "smtp":
courierChannel, err := NewSMTPChannelWithCustomTemplates(c.deps, channel.SMTPConfig, c.newEmailTemplateFromMessage)
if err != nil {
return nil, err
}
return courierChannel, nil
case "http":
return newHttpChannel(channel.ID, &channel.RequestConfig, c.deps), nil
default:
return nil, errors.Errorf("unknown courier channel type: %s", channel.Type)
}
}
return nil, errors.Errorf("no courier channels configured for: %s", id)
}
func (c *courier) DispatchMessage(ctx context.Context, msg Message) (err error) {
ctx, span := c.deps.Tracer(ctx).Tracer().Start(ctx, "courier.DispatchMessage", trace.WithAttributes(
attribute.Stringer("message.id", msg.ID),
attribute.Stringer("message.nid", msg.NID),
attribute.Stringer("message.type", msg.Type),
attribute.String("message.template_type", string(msg.TemplateType)),
attribute.Int("message.send_count", msg.SendCount),
))
defer otelx.End(span, &err)
ctx = semconv.ContextWithAttributes(ctx, semconv.AttrNID(msg.NID))
logger := c.deps.Logger().
WithField("message_id", msg.ID).
WithField("message_nid", msg.NID).
WithField("message_type", msg.Type).
WithField("message_template_type", msg.TemplateType).View on GitHub (pinned to b86338da04)