sipeed/picoclaw · error

slack_webhook: at least one webhook target is required

Error message

slack_webhook: at least one webhook target is required

What it means

NewSlackWebhookChannel rejects a slack_webhook channel whose settings have no webhooks entries at all (config key webhooks, a map of SlackWebhookTarget, pkg/config/config.go:743). The channel is outgoing-only and delivers exclusively via Slack incoming webhooks, so an empty map leaves it with nothing to send to; construction fails fast.

Source

Thrown at pkg/channels/slack_webhook/slack_webhook.go:39

const maxTextBlockLength = 3000

// SlackWebhookChannel is an output-only channel that sends messages
// to Slack via Incoming Webhooks using Block Kit formatting.
type SlackWebhookChannel struct {
	*channels.BaseChannel
	bc     *config.Channel
	config *config.SlackWebhookSettings
	client *http.Client
}

// NewSlackWebhookChannel creates a new Slack webhook channel.
func NewSlackWebhookChannel(
	bc *config.Channel,
	cfg *config.SlackWebhookSettings,
	bus *bus.MessageBus,
) (*SlackWebhookChannel, error) {
	if len(cfg.Webhooks) == 0 {
		return nil, fmt.Errorf("slack_webhook: at least one webhook target is required")
	}

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

	for name, target := range cfg.Webhooks {
		webhookURL := target.WebhookURL.String()
		if webhookURL == "" {
			return nil, fmt.Errorf("slack_webhook: webhook %q has empty webhook_url", name)
		}
		parsed, err := url.Parse(webhookURL)
		if err != nil {
			return nil, fmt.Errorf("slack_webhook: webhook %q has invalid URL format: %w", name, err)
		}
		if !strings.EqualFold(parsed.Scheme, "https") {
			return nil, fmt.Errorf("slack_webhook: webhook %q must use HTTPS (got %q)", name, parsed.Scheme)
		}

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Create an incoming webhook in the Slack app (Features > Incoming Webhooks), copy the https://hooks.slack.com/services/... URL, and add it under webhooks with at least a default target (see the companion 'default required' error).
  2. Check YAML shape: webhooks must be a map directly under the channel's settings with each target having webhook_url.
  3. If you meant to build a two-way Slack bot, use type: slack with bot/app tokens instead of slack_webhook.

Example fix

# before
channels:
  notify:
    type: slack_webhook
    settings: {}          # no webhooks

# after
channels:
  notify:
    type: slack_webhook
    settings:
      webhooks:
        default:
          webhook_url: "https://hooks.slack.com/services/T000/B000/xxxx"
Defensive patterns

Strategy: validation

Validate before calling

func slackWebhookTargetsPresent(cfg *config.SlackWebhookSettings) error {
    if len(cfg.Webhooks) == 0 {
        return fmt.Errorf("add at least one webhooks.<name>.webhook_url (plus 'default')")
    }
    return nil
}

Type guard

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

Try / catch

// Go: synchronous constructor error — validate config in deploy pipeline; no runtime catch needed

Prevention

When it happens

Trigger: Creating a channel of type slack_webhook with no webhooks: key in settings; webhooks: {} (empty map); webhook entries indented under the wrong parent so the map parses as empty; using slack_webhook where slack (Socket Mode) was intended.

Common situations: Configuring outgoing notifications but not yet having created the incoming webhook in the Slack app; YAML indentation placing webhook entries under a sibling key; migrating from a single webhook_url field to the webhooks map and leaving it empty.

Related errors


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