ory/kratos · error

unknown courier channel type

Error message

unknown courier channel type: %s

What it means

The courier dispatcher resolves a configured channel by ID, switching on channel.Type. Only sms, email (implied cases) and "http" are supported; any other type string makes the channels method return this error. It signals an invalid channel type in the courier configuration.

Solutions

  1. Set the channel type to a supported value: sms, email, or http.
  2. Fix typos in the courier config's channels[].type field.
  3. Check the docs/version for the exact set of supported channel types in your release.
  4. Validate the config before startup to catch the bad type early.

Example fix

// before (config.yaml)
courier:
  channels:
    - id: sms
      type: smss
// after
courier:
  channels:
    - id: sms
      type: http
      request_config: {...}
Defensive patterns

Strategy: validation

Validate before calling

supported := map[string]bool{"sms": true, "email": true, "http": true}
for _, ch := range cfg.Courier.Channels {
    if !supported[ch.Type] {
        return fmt.Errorf("channel %q has unsupported type %q", ch.ID, ch.Type)
    }
}

Type guard

func isSupportedChannelType(t string) bool {
    switch t { case "sms", "email", "http": return true }
    return false
}

Try / catch

if err := courier.DispatchMessage(ctx, msg); err != nil {
    if strings.Contains(err.Error(), "unknown courier channel type") {
        log.Printf("config error: %v", err) // fix courier.channels types
        return nil // or dead-letter the message
    }
    return err
}

Prevention

When it happens

Trigger: Courier config contains a channel whose type is not one of the recognized values (e.g. type: webhook or a typo like htpp/smss), and a message is dispatched to that channel ID.

Common situations: Typos in the YAML/JSON config channel type, copying config snippets from newer/older versions where a channel type was added or removed, or inventing a custom type expecting generic HTTP behavior.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of ory/kratos@b86338da04 (2026-09-07). Data as JSON: /api/errors/668ecfda2bacb4d7. Report an issue: GitHub.

Appendix: source

Thrown at courier/courier_dispatcher.go:39

	if err != nil {
		return nil, err
	}

	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().

View on GitHub (pinned to b86338da04)