amir20/dozzle · error

cloud: alerts

Error message

cloud: alerts: %w

What it means

GetAlerts calls the cloud gRPC service (ListAlerts-style request with time range, limit, and include flags) and wraps any RPC failure with 'cloud: alerts: %w'. The cloud backend is unreachable, rejected the request, or returned an error. The wrapper preserves the underlying gRPC status for inspection.

Solutions

  1. Read the wrapped error with errors.Is/errors.As to check the gRPC status code
  2. Verify network connectivity and DNS for the cloud endpoint
  3. Confirm the cloud API key is valid and the account has an active pro plan (PermissionDenied stops retries)
  4. Retry with backoff; the cloud client already reconnects with exponential backoff for transient failures
Defensive patterns

Strategy: retry

Try / catch

hits, err := client.GetAlerts(ctx, req)
if err != nil {
    if status.Code(err) == codes.PermissionDenied {
        return nil, err // do not retry: invalid API key / no pro plan
    }
    return nil, fmt.Errorf("fetching alerts, will retry: %w", err)
}

Prevention

When it happens

Trigger: Calling GetAlerts while the unary cloud connection fails: network outage, invalid API key returning PermissionDenied, server-side error, or context deadline exceeded during the RPC.

Common situations: Dozzle Cloud endpoint down or unreachable, expired/billing-deactivated API key, firewall blocking egress to the cloud service, timeouts on slow responses.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07). Data as JSON: /api/errors/26449ca295a5199f. Report an issue: GitHub.

Appendix: source

Thrown at internal/cloud/alerts.go:118

	}

	mdPairs := []string{"x-api-key", apiKey}
	if c.instanceID != "" {
		mdPairs = append(mdPairs, "x-instance-id", c.instanceID)
	}
	callCtx := metadata.NewOutgoingContext(ctx, metadata.Pairs(mdPairs...))

	resp, err := client.GetAlerts(callCtx, &pb.GetAlertsRequest{
		ContainerIds:     containerIDs,
		HostId:           hostID,
		FromTsNs:         fromNs,
		ToTsNs:           toNs,
		Limit:            limit,
		IncludeFollowUps: includeFollowUps,
		IncludeEvents:    includeEvents,
	})
	if err != nil {
		return nil, fmt.Errorf("cloud: alerts: %w", err)
	}

	hits := make([]AlertHit, 0, len(resp.GetHits()))
	for _, h := range resp.GetHits() {
		hits = append(hits, AlertHit{
			AlertID:         h.GetAlertId(),
			ContainerID:     h.GetContainerId(),
			HostID:          h.GetHostId(),
			LogID:           h.GetLogId(),
			Ts:              h.GetAnchorTsNs(),
			Headline:        h.GetHeadline(),
			Level:           h.GetLevel(),
			EventCount:      h.GetEventCount(),
			SuppressedCount: h.GetSuppressedCount(),
			ContainerCount:  h.GetContainerCount(),
			Summary:         h.GetSummary(),
			Investigation:   h.GetInvestigation(),
			TriageAction:    h.GetTriageAction(),

View on GitHub (pinned to d9463cbe21)