amir20/dozzle · error

failed to send to cloud

Error message

failed to send to cloud: %w

What it means

This wraps the error returned by the HTTP client's Do() call when the POST to the Dozzle Cloud /api/events endpoint fails at the transport level. It means no HTTP response was received: connection refused, DNS failure, TLS error, timeout (client timeout is 10s), or context cancellation.

Solutions

  1. Inspect the wrapped %w cause with errors.Unwrap or %v to identify connection vs timeout vs DNS
  2. Verify outbound network/DNS from the Dozzle container (docker exec ... wget https://doligence.dozzle.dev)
  3. Check the DOLIGENCE_URL env var points to a reachable host if overridden
  4. Retry with backoff; transport errors are transient unless the endpoint is permanently blocked by firewall

Example fix

if err := d.Send(ctx, n); err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) && netErr.Timeout() {
        // retry with backoff
    }
}
Defensive patterns

Strategy: retry

Try / catch

var nErr net.Error
if errors.As(err, &nErr) && nErr.Timeout() {
    // exponential backoff retry
} else {
    // check connectivity/DNS before retrying
}

Prevention

When it happens

Trigger: Calling CloudDispatcher.Send when the network is down, DNS cannot resolve the cloud host, the TLS handshake fails, the 10s client timeout expires, or ctx is cancelled mid-request.

Common situations: Container has no outbound internet access; corporate proxy/firewall blocks doligence.dozzle.dev; DNS misconfiguration in Docker networks; a custom DOLIGENCE_URL pointing at an unreachable host.

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 amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/d80407b274e33ff7. Report an issue: GitHub.

Appendix: source

Thrown at internal/notification/dispatcher/cloud.go:94

	}

	payload, err := json.Marshal(notification)
	if err != nil {
		return fmt.Errorf("failed to marshal notification: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.URL, bytes.NewReader(payload))
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("User-Agent", UserAgent)
	req.Header.Set("X-API-Key", c.APIKey)

	resp, err := c.client.Do(req)
	if err != nil {
		return fmt.Errorf("failed to send to cloud: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusTooManyRequests {
		retryAfter := defaultRetryAfter
		if ra := resp.Header.Get("Retry-After"); ra != "" {
			if seconds, err := strconv.Atoi(ra); err == nil {
				retryAfter = time.Duration(seconds) * time.Second
			}
		}
		c.blockedUntil.Store(time.Now().Add(retryAfter).UnixNano())
		log.Warn().
			Str("cloud", c.Name).
			Dur("retry_after", retryAfter).
			Msg("rate limited by cloud, circuit breaker tripped")
		return fmt.Errorf("cloud rate limited, backing off for %s", retryAfter)
	}

View on GitHub (pinned to d9463cbe21)