sipeed/picoclaw · error

teams_webhook: at least one webhook target is required

Error message

teams_webhook: at least one webhook target is required

What it means

NewTeamsWebhookChannel rejects a teams_webhook channel whose settings contain no webhook targets (config key webhooks, map[string]TeamsWebhookTarget, pkg/config/config.go:721). Like slack_webhook, the channel is outgoing-only and delivers exclusively via Microsoft Teams Workflows/Connectors incoming webhooks, so an empty map leaves nothing to send to and construction fails fast.

Source

Thrown at pkg/channels/teams_webhook/teams_webhook.go:67

// TeamsWebhookChannel is an output-only channel that sends messages
// to Microsoft Teams via Power Automate workflow webhooks.
// Multiple webhook targets can be configured and selected via ChatID.
type TeamsWebhookChannel struct {
	*channels.BaseChannel
	bc     *config.Channel
	config *config.TeamsWebhookSettings
	client teamsMessageSender
}

// NewTeamsWebhookChannel creates a new Teams webhook channel.
func NewTeamsWebhookChannel(
	bc *config.Channel,
	cfg *config.TeamsWebhookSettings,
	bus *bus.MessageBus,
) (*TeamsWebhookChannel, error) {
	if len(cfg.Webhooks) == 0 {
		return nil, fmt.Errorf("teams_webhook: at least one webhook target is required")
	}

	// Require "default" webhook target
	if _, hasDefault := cfg.Webhooks["default"]; !hasDefault {
		return nil, fmt.Errorf("teams_webhook: a 'default' webhook target is required")
	}

	// Validate all webhook targets have valid HTTPS URLs
	for name, target := range cfg.Webhooks {
		webhookURL := target.WebhookURL.String()
		if webhookURL == "" {
			return nil, fmt.Errorf("teams_webhook: webhook %q has empty webhook_url", name)
		}
		parsed, err := url.Parse(webhookURL)
		if err != nil {
			return nil, fmt.Errorf("teams_webhook: webhook %q has invalid URL: %w", name, err)
		}
		if !strings.EqualFold(parsed.Scheme, "https") {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Create an incoming webhook in Teams (channel > Workflows/Connectors > incoming webhook) and copy the https://...webhook.office.com/... (or outlook.office365.com/webhook/...) URL.
  2. Add it under webhooks with at least a default target — the constructor also requires a 'default' key and HTTPS URLs for every target.
  3. Verify YAML nesting: webhooks sits under the channel's settings, each target is a map key with webhook_url.
  4. If a full Teams bot (messages in, not just notifications out) is needed, use the teams channel type instead.

Example fix

# before
channels:
  msteams:
    type: teams_webhook
    settings: {}

# after
channels:
  msteams:
    type: teams_webhook
    settings:
      webhooks:
        default:
          webhook_url: "https://prod-XX.region.logic.azure.com/workflows/.../triggers/manual/paths/invoke"
Defensive patterns

Strategy: validation

Validate before calling

func teamsWebhookTargetsPresent(cfg *config.TeamsWebhookSettings) error {
    if len(cfg.Webhooks) == 0 {
        return fmt.Errorf("add webhooks.default.webhook_url (Teams Workflows URL) before enabling teams_webhook")
    }
    return nil
}

Type guard

// structural guard before construction
func hasTeamsWebhookMap(v any) bool {
    m, ok := v.(map[string]config.TeamsWebhookTarget)
    return ok && len(m) > 0
}

Try / catch

// Go: synchronous constructor error — validate config in the deploy pipeline; nothing to catch at message-send time

Prevention

When it happens

Trigger: Creating a channel of type teams_webhook with no webhooks: key, webhooks: {}, or entries indented under the wrong parent key so the map decodes empty; using teams_webhook when a two-way Teams bot channel was intended.

Common situations: Not having created the Teams incoming webhook URL (Power Automate Workflows 'When a webhook request is received' or legacy Office 365 connector) before configuring the channel; migrating from a single-url field to the webhooks map; YAML indentation mistakes after copy-pasting a slack_webhook block as a template.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/eb3009819f5d402f. Report an issue: GitHub.