{"record":{"id":"9245255877f9e37b","repo":"sipeed/picoclaw","slug":"slack-webhook-at-least-one-webhook-target-is-requ","errorCode":null,"errorMessage":"slack_webhook: at least one webhook target is required","messagePattern":"slack_webhook: at least one webhook target is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/channels/slack_webhook/slack_webhook.go","lineNumber":39,"sourceCode":"const maxTextBlockLength = 3000\n\n// SlackWebhookChannel is an output-only channel that sends messages\n// to Slack via Incoming Webhooks using Block Kit formatting.\ntype SlackWebhookChannel struct {\n\t*channels.BaseChannel\n\tbc     *config.Channel\n\tconfig *config.SlackWebhookSettings\n\tclient *http.Client\n}\n\n// NewSlackWebhookChannel creates a new Slack webhook channel.\nfunc NewSlackWebhookChannel(\n\tbc *config.Channel,\n\tcfg *config.SlackWebhookSettings,\n\tbus *bus.MessageBus,\n) (*SlackWebhookChannel, error) {\n\tif len(cfg.Webhooks) == 0 {\n\t\treturn nil, fmt.Errorf(\"slack_webhook: at least one webhook target is required\")\n\t}\n\n\tif _, hasDefault := cfg.Webhooks[\"default\"]; !hasDefault {\n\t\treturn nil, fmt.Errorf(\"slack_webhook: a 'default' webhook target is required\")\n\t}\n\n\tfor name, target := range cfg.Webhooks {\n\t\twebhookURL := target.WebhookURL.String()\n\t\tif webhookURL == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"slack_webhook: webhook %q has empty webhook_url\", name)\n\t\t}\n\t\tparsed, err := url.Parse(webhookURL)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"slack_webhook: webhook %q has invalid URL format: %w\", name, err)\n\t\t}\n\t\tif !strings.EqualFold(parsed.Scheme, \"https\") {\n\t\t\treturn nil, fmt.Errorf(\"slack_webhook: webhook %q must use HTTPS (got %q)\", name, parsed.Scheme)\n\t\t}","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/channels/slack_webhook/slack_webhook.go#L21-L57","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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).","Check YAML shape: webhooks must be a map directly under the channel's settings with each target having webhook_url.","If you meant to build a two-way Slack bot, use type: slack with bot/app tokens instead of slack_webhook."],"exampleFix":"# before\nchannels:\n  notify:\n    type: slack_webhook\n    settings: {}          # no webhooks\n\n# after\nchannels:\n  notify:\n    type: slack_webhook\n    settings:\n      webhooks:\n        default:\n          webhook_url: \"https://hooks.slack.com/services/T000/B000/xxxx\"","handlingStrategy":"validation","validationCode":"func slackWebhookTargetsPresent(cfg *config.SlackWebhookSettings) error {\n    if len(cfg.Webhooks) == 0 {\n        return fmt.Errorf(\"add at least one webhooks.<name>.webhook_url (plus 'default')\")\n    }\n    return nil\n}","typeGuard":"// structural guard before channel construction\nfunc hasWebhookMap(v any) bool {\n    m, ok := v.(map[string]config.SlackWebhookTarget)\n    return ok && len(m) > 0\n}","tryCatchPattern":"// Go: synchronous constructor error — validate config in deploy pipeline; no runtime catch needed","preventionTips":["Create the Slack incoming webhook URL before enabling the channel","Remember the webhooks map is required even for a single target","slack_webhook is outgoing-only; choose type: slack for two-way bots"],"tags":["slack-webhook","config","channel-setup","startup"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}