{"record":{"id":"f387c0b2ed094074","repo":"amir20/dozzle","slug":"invalid-webhook-url-w","errorCode":null,"errorMessage":"invalid webhook URL: %w","messagePattern":"invalid webhook URL: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/notification/dispatcher/webhook.go","lineNumber":165,"sourceCode":"// UserAgent is set by the application at startup\nvar UserAgent = \"Dozzle/head\"\n\n// WebhookDispatcher sends notifications to a webhook URL\ntype WebhookDispatcher struct {\n\tName         string\n\tURL          string\n\tTemplate     *template.Template\n\tTemplateText string // Original template string for serialization\n\tHeaders      map[string]string\n\tclient       *http.Client\n}\n\n// NewWebhookDispatcher creates a new webhook dispatcher\n// If templateStr is empty, the notification will be marshaled as JSON directly\nfunc NewWebhookDispatcher(name, rawURL, templateStr string, headers map[string]string) (*WebhookDispatcher, error) {\n\tparsed, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid webhook URL: %w\", err)\n\t}\n\tif scheme := strings.ToLower(parsed.Scheme); scheme != \"http\" && scheme != \"https\" {\n\t\treturn nil, fmt.Errorf(\"invalid webhook URL scheme %q: only http and https are allowed\", parsed.Scheme)\n\t}\n\n\tw := &WebhookDispatcher{\n\t\tName:         name,\n\t\tURL:          rawURL,\n\t\tTemplateText: templateStr,\n\t\tHeaders:      headers,\n\t\tclient: &http.Client{\n\t\t\tTimeout: 10 * time.Second,\n\t\t\tTransport: &http.Transport{\n\t\t\t\tDialContext:           safeDialContext,\n\t\t\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\t\t\tResponseHeaderTimeout: 10 * time.Second,\n\t\t\t\tExpectContinueTimeout: 1 * time.Second,\n\t\t\t},","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/notification/dispatcher/webhook.go#L147-L183","documentation":"NewWebhookDispatcher validates the raw webhook URL with url.Parse before constructing the dispatcher. If Go's URL parser cannot parse the string at all (e.g. control characters, malformed percent-encoding), it returns this wrapped error and no dispatcher is created.","triggerScenarios":"Calling NewWebhookDispatcher (or createDispatcher for a webhook destination) with a rawURL string that net/url.Parse rejects, such as one containing invalid percent escapes or control bytes.","commonSituations":"Webhook URL pasted into the UI with stray whitespace/newlines or copied from a rich-text source; unescaped '%' characters; config file YAML values containing invisible characters.","solutions":["Inspect the wrapped parse error for the exact offending position","Re-enter the URL cleanly, trimming whitespace and control characters","Percent-encode special characters properly (use url.PathEscape / QueryEscape on components)","If the URL comes from YAML/env, check for line-folding or BOM artifacts"],"exampleFix":"// before\nNewWebhookDispatcher(\"hook\", \"https://example.com/hook?x=100%\", \"\", nil)\n// after\nNewWebhookDispatcher(\"hook\", \"https://example.com/hook?x=100%25\", \"\", nil)","handlingStrategy":"validation","validationCode":"u, err := url.Parse(strings.TrimSpace(rawURL))\nif err != nil {\n    return fmt.Errorf(\"webhook URL is not parseable: %w\", err)\n}","typeGuard":"func validURL(raw string) bool {\n    _, err := url.Parse(strings.TrimSpace(raw))\n    return err == nil\n}","tryCatchPattern":"d, err := NewWebhookDispatcher(name, rawURL, tpl, headers)\nif err != nil {\n    return fmt.Errorf(\"webhook config invalid: %w\", err)\n}","preventionTips":["Trim and sanitize URLs from UI/config input before constructing dispatchers","Avoid manual string concatenation into URLs; escape components","Validate URLs at config-save time in your own UI"],"tags":["url","validation","webhook","config"],"backgroundTag":"invalid-url","analyzedSha":"d9463cbe21874e44ab79db6fa63e746ca7d22928","analyzedAt":"2026-09-07T10:08:55.855Z","contentChangedAt":"2026-09-07T10:08:55.855Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}