{"record":{"id":"570ca9ad0e55c129","repo":"micro/go-micro","slug":"push-callback-host-q-did-not-resolve","errorCode":null,"errorMessage":"push callback host %q did not resolve","messagePattern":"push callback host %q did not resolve","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gateway/a2a/pushsecurity.go","lineNumber":47,"sourceCode":"// defaultPushURLPolicy is the SSRF-safe policy applied when no AllowPushURL is\n// configured. It rejects non-http(s) schemes and hosts that resolve to a\n// loopback, private, link-local, multicast, or unspecified address.\nfunc defaultPushURLPolicy(u *url.URL) error {\n\tswitch u.Scheme {\n\tcase \"http\", \"https\":\n\tdefault:\n\t\treturn fmt.Errorf(\"push callback scheme %q not allowed (want http or https)\", u.Scheme)\n\t}\n\thost := u.Hostname()\n\tif host == \"\" {\n\t\treturn fmt.Errorf(\"push callback url has no host\")\n\t}\n\tips, err := resolvePushHost(host)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"push callback host %q: %w\", host, err)\n\t}\n\tif len(ips) == 0 {\n\t\treturn fmt.Errorf(\"push callback host %q did not resolve\", host)\n\t}\n\tfor _, ip := range ips {\n\t\tif blockedPushIP(ip) {\n\t\t\treturn fmt.Errorf(\"push callback host %q resolves to a blocked address %s\", host, ip)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc resolvePushHost(host string) ([]net.IP, error) {\n\tif ip := net.ParseIP(host); ip != nil {\n\t\treturn []net.IP{ip}, nil\n\t}\n\treturn pushLookupIP(host)\n}\n\n// blockedPushIP reports whether ip is one an outbound push callback must not\n// reach: loopback, private (RFC1918 / ULA), link-local (incl. 169.254.169.254","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/gateway/a2a/pushsecurity.go#L29-L65","documentation":"defaultPushURLPolicy requires that a syntactically valid callback host resolve to at least one IP address. This error is thrown when DNS resolution succeeds (no error) but returns an empty result set, meaning the name is defined but has no usable address records. The policy cannot authorize a callback to a host with no addresses.","triggerScenarios":"Calling SetPushNotificationConfig with a hostname whose DNS records were just removed or that only has records of types the resolver ignores (e.g. CNAME pointing to a dead target).","commonSituations":"Webhook service decommissioned but DNS entry left behind; dangling CNAME; TTL-expired records removed during infra migration while applications still reference the old host.","solutions":["Check DNS records for the host (dig A/AAAA/CNAME) and point it at a live address or update the callback URL.","Update the push config to a currently live webhook endpoint.","If records were just changed, retry after the DNS TTL expires."],"exampleFix":"// before\ncallback := \"https://old-webhook.example.com/hook\" // A records removed\n// after\ncallback := \"https://webhook.example.com/hook\" // live endpoint","handlingStrategy":"validation","validationCode":"addrs, err := net.LookupHost(host)\nif err != nil || len(addrs) == 0 {\n\treturn fmt.Errorf(\"callback host %q has no address records\", host)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Monitor DNS records for webhook hosts and alert on removal","Clean up callbacks pointing at decommissioned services","Prefer direct A/AAAA records over dangling CNAME chains"],"tags":["dns","network","push-notifications"],"backgroundTag":"dns-resolution-failed","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}