sipeed/picoclaw · error
slack_webhook: webhook %q has invalid URL format: %w
Error message
slack_webhook: webhook %q has invalid URL format: %w
What it means
During webhook validation, each webhook_url is passed through url.Parse; a parse error is wrapped as "webhook %q has invalid URL format". Go's url.Parse only errors on genuinely malformed input — control characters, unmatched brackets in the host, or a URL that is not absolute when later used as a request target — so this fires on corrupted values rather than merely unusual ones.
Source
Thrown at pkg/channels/slack_webhook/slack_webhook.go:53
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",
cfg,
bus,
[]string{"*"},
channels.WithMaxMessageLength(40000),
)
return &SlackWebhookChannel{
BaseChannel: base,
bc: bc,
config: cfg,View on GitHub (pinned to 49183d7e8d)
Solutions
- Re-paste the webhook URL as plain text from the Slack app settings page; strip stray whitespace, quotes, and control characters.
- The wrapped error names the byte offset/reason — read errors.Unwrap(err) (a *url.Error) to find what character is offending.
- Quote the value in YAML (webhook_url: "https://...") to avoid parser-induced mutations.
Example fix
// before
url := webhookURLFromUser // may contain \r\n from clipboard
// after: sanitize before saving config
cleaned := strings.TrimSpace(strings.Map(func(r rune) rune {
if r < 32 { return -1 } // strip control chars
return r
}, raw))
if _, err := url.Parse(cleaned); err != nil { return fmt.Errorf("bad webhook_url: %w", err) } Defensive patterns
Strategy: validation
Validate before calling
func validWebhookURL(raw string) error {
u, err := url.Parse(strings.TrimSpace(raw))
if err != nil { return fmt.Errorf("unparseable webhook_url: %w", err) }
if u.Scheme == "" || u.Host == "" { return fmt.Errorf("webhook_url must be absolute") }
return nil
} Try / catch
// Go: if err wraps *url.Error (errors.As) -> extract and log the parse reason; fail config validation, not runtime
Prevention
- Paste webhook URLs as plain text; rich-text editors inject smart quotes/invisibles
- Strip control characters before saving config values
- Quote URL values in YAML to avoid parser mutations
When it happens
Trigger: webhook_url containing raw control characters (\x00, \r), a value wrapped in quotes/smart quotes copied from a rich-text editor, embedded newlines from terminal line-wrapping, or a truncated paste that leaves an unbalanced URL; note a missing scheme alone does not fail url.Parse here — it is caught by the HTTPS check instead.
Common situations: Copying URLs from chat/email clients that insert invisible characters or line breaks; YAML block scalars accidentally including trailing garbage; encoding mishaps (non-UTF8 bytes) after editing config on different platforms; placeholder text like ${WEBHOOK_URL} left unreplaced is usually caught by the HTTPS check, not this one.
Related errors
- MCP server ${server.name} requires a URL.
- MCP server ${server.name} requires a valid HTTP(S) URL.
- invalid WeCom QR generate URL: %w
- invalid WeCom QR query URL: %w
- invalid WeCom QR page URL: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/7be90d56301d1d0a.
Report an issue: GitHub.