sipeed/picoclaw · error
slack_webhook: a 'default' webhook target is required
Error message
slack_webhook: a 'default' webhook target is required
What it means
NewSlackWebhookChannel requires that the webhooks map contains a key literally named "default". The default target is the fallback used when an outbound message's ChatID names an unknown target (the code logs 'Unknown target, falling back to default' and uses Webhooks["default"]). Without it, fallback sends would panic or fail, so construction refuses.
Source
Thrown at pkg/channels/slack_webhook/slack_webhook.go:43
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)
}
}
base := channels.NewBaseChannel(
"slack_webhook",View on GitHub (pinned to 49183d7e8d)
Solutions
- Add a default: entry under webhooks with a valid https://hooks.slack.com/services/... URL.
- Point default at your primary webhook if you have several targets; named targets remain addressable via ChatID.
- Keep the literal lowercase key 'default' — it is matched exactly, not case-insensitively.
Example fix
# before
settings:
webhooks:
ops:
webhook_url: "https://hooks.slack.com/services/T/B/ops"
# after
settings:
webhooks:
default:
webhook_url: "https://hooks.slack.com/services/T/B/main" # fallback
ops:
webhook_url: "https://hooks.slack.com/services/T/B/ops" Defensive patterns
Strategy: validation
Validate before calling
func hasDefaultWebhook(webhooks map[string]config.SlackWebhookTarget) bool {
t, ok := webhooks["default"]
return ok && t.WebhookURL.String() != ""
} Type guard
// exact-key guard
func defaultTarget(webhooks map[string]config.SlackWebhookTarget) (config.SlackWebhookTarget, bool) {
t, ok := webhooks["default"]
return t, ok // key match is case-sensitive
} Try / catch
// Go: if err contains "'default' webhook target is required" -> add webhooks.default.webhook_url; construction-time only
Prevention
- Always configure webhooks.default first, then named targets
- The key must be the literal lowercase 'default'
- Keep default pointed at a webhook you monitor — fallbacks land there
When it happens
Trigger: webhooks populated with custom names (team-a, ops, etc.) but no default entry; the fallback map lookup c.config.Webhooks["default"] would otherwise return a zero-value target at send time.
Common situations: Copying an example config that only shows named targets; renaming 'default' to something descriptive; deleting the default entry after adding named targets while messages still arrive without an explicit target.
Related errors
- slack_webhook: webhook %q has empty webhook_url
- slack_webhook: webhook %q has invalid URL format: %w
- slack_webhook: webhook %q must use HTTPS (got %q)
- ${label} must be a JSON object.
- ${label}.${key} must be a string.
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/0cc93675c8c5ae46.
Report an issue: GitHub.