knadh/listmonk · error

error requesting subscription URL: %v

Error message

error requesting subscription URL: %v

What it means

After parsing and signature-verifying an SNS subscription (un)confirmation, ProcessSubscription must visit the SubscribeURL (or UnsubscribeURL) with HTTP GET to complete the confirmation. If the HTTP GET itself fails at the transport level (DNS, TLS, connection refused, timeout), the handler returns 'error requesting subscription URL'.

Source

Thrown at internal/bounce/webhooks/ses.go:99

// by parsing and verifying the payload and calling the subscribe / unsubscribe URL.
func (s *SES) ProcessSubscription(b []byte) error {
	var n sesNotif
	if err := json.Unmarshal(b, &n); err != nil {
		return fmt.Errorf("error unmarshalling SNS notification: %v", err)
	}
	if err := s.verifyNotif(n); err != nil {
		return err
	}

	// Make an HTTP request to the sub/unsub URL.
	u := n.SubscribeURL
	if n.Type == "UnsubscriptionConfirmation" {
		u = n.UnsubscribeURL
	}

	resp, err := http.Get(u)
	if err != nil {
		return fmt.Errorf("error requesting subscription URL: %v", err)
	}

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("non 200 response on subscription URL: %v", resp.StatusCode)
	}

	return nil
}

// ProcessBounce processes an SES bounce notification and returns a Bounce object.
func (s *SES) ProcessBounce(b []byte) (models.Bounce, error) {
	var (
		bounce models.Bounce
		n      sesNotif
	)
	if err := json.Unmarshal(b, &n); err != nil {
		return bounce, fmt.Errorf("error unmarshalling SES notification: %v", err)
	}

View on GitHub (pinned to 670c01717d)

Solutions

  1. Verify the server can curl the SubscribeURL shown in the SNS console/notification directly
  2. Open outbound HTTPS (443) egress to *.amazonaws.com from the listmonk host/container
  3. If a proxy is required, configure it via HTTP_PROXY/HTTPS_PROXY env vars so the default transport honors it
  4. Fix DNS resolution on the host (check resolv.conf, VPC DNS, or CoreDNS in Kubernetes)
  5. Retry the subscription — SNS will re-send SubscriptionConfirmation notifications

Example fix

// before: run container with no egress
docker run --network internal listmonk
// after: allow outbound 443 or use a proxy env
HTTPS_PROXY=http://proxy.corp:3128 docker run listmonk
Defensive patterns

Strategy: retry

Validate before calling

u := "https://sns.<region>.amazonaws.com/?Action=ConfirmSubscription..."
host, err := url.Parse(u)
if err != nil { return false }
addrs, err := net.LookupHost(host.Hostname())
return err == nil && len(addrs) > 0
// pre-flight: resolve the SubscribeURL host before processing

Try / catch

err := handler.ProcessSubscription(body)
if err != nil {
    if strings.HasPrefix(err.Error(), "error requesting subscription URL") {
        log.Printf("SNS confirmation URL unreachable, will rely on SNS retry: %v", err)
        // SNS re-sends confirmations; return 500 so retries/schedule re-check
        http.Error(w, "subscription URL unreachable", http.StatusBadGateway)
        return
    }
    http.Error(w, "webhook error", http.StatusInternalServerError)
}

Prevention

When it happens

Trigger: http.Get(n.SubscribeURL or n.UnsubscribeURL) returns a non-nil error: no outbound internet/DNS on the server, egress firewall blocking sns.<region>.amazonaws.com, TLS interception, IPv6 issues, or a malformed/unreachable URL in a forged-but-signature-valid payload.

Common situations: listmonk deployed in a restricted network (Docker/K8s) without outbound internet access; corporate proxy required but not configured (HTTP_PROXY ignored by http.Get default transport); DNS resolution failures; SES in a region blocked by network policy.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/e401c70c77cc360b. Report an issue: GitHub.