{"record":{"id":"b669a076325263be","repo":"amir20/dozzle","slug":"cloud-rate-limited-backing-off-for-s","errorCode":null,"errorMessage":"cloud rate limited, backing off for %s","messagePattern":"cloud rate limited, backing off for (.+?)","errorType":"exception","errorClass":null,"httpStatus":429,"severity":"warning","filePath":"internal/notification/dispatcher/cloud.go","lineNumber":110,"sourceCode":"\tresp, err := c.client.Do(req)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to send to cloud: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode == http.StatusTooManyRequests {\n\t\tretryAfter := defaultRetryAfter\n\t\tif ra := resp.Header.Get(\"Retry-After\"); ra != \"\" {\n\t\t\tif seconds, err := strconv.Atoi(ra); err == nil {\n\t\t\t\tretryAfter = time.Duration(seconds) * time.Second\n\t\t\t}\n\t\t}\n\t\tc.blockedUntil.Store(time.Now().Add(retryAfter).UnixNano())\n\t\tlog.Warn().\n\t\t\tStr(\"cloud\", c.Name).\n\t\t\tDur(\"retry_after\", retryAfter).\n\t\t\tMsg(\"rate limited by cloud, circuit breaker tripped\")\n\t\treturn fmt.Errorf(\"cloud rate limited, backing off for %s\", retryAfter)\n\t}\n\n\tif resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {\n\t\tlimitedReader := io.LimitReader(resp.Body, 1024*1024)\n\t\tresponseBody, _ := io.ReadAll(limitedReader)\n\t\tc.blockedUntil.Store(time.Now().Add(unauthorizedRetryAfter).UnixNano())\n\t\tlog.Warn().\n\t\t\tStr(\"cloud\", c.Name).\n\t\t\tInt(\"status_code\", resp.StatusCode).\n\t\t\tDur(\"retry_after\", unauthorizedRetryAfter).\n\t\t\tMsg(\"cloud rejected API key, circuit breaker tripped\")\n\t\treturn fmt.Errorf(\"cloud returned status code %d: %s\", resp.StatusCode, string(responseBody))\n\t}\n\n\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\tlimitedReader := io.LimitReader(resp.Body, 1024*1024)\n\t\tresponseBody, _ := io.ReadAll(limitedReader)\n\t\tlog.Debug().","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/amir20/dozzle/blob/d9463cbe21874e44ab79db6fa63e746ca7d22928/internal/notification/dispatcher/cloud.go#L92-L128","documentation":"Dozzle Cloud responded with HTTP 429 Too Many Requests. The dispatcher stores a blockedUntil timestamp (Retry-After header if present, otherwise 60s), logs a warning that the circuit breaker tripped, and returns this error for the current notification, which is dropped.","triggerScenarios":"CloudDispatcher.Send received resp.StatusCode == 429 from the cloud /api/events endpoint, i.e. the account exceeded the cloud-side notification rate limit.","commonSituations":"Notification rules that fire on every log line; many containers emitting alerts simultaneously; retry loops without backoff hammering the endpoint; shared/demo API keys near quota.","solutions":["Respect the backoff: don't resend until the breaker window (Retry-After or default 60s) passes","Throttle or debounce notification rules so volume stays under the cloud quota","Batch related notifications instead of sending one per event","Check your Dozzle Cloud plan limits if 429s recur regularly"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := d.Send(ctx, n); err != nil {\n    if strings.Contains(err.Error(), \"backing off\") {\n        // respect the 60s+ backoff; queue and resend later\n    }\n}","preventionTips":["Keep notification volume below cloud quota; batch and debounce alerts","Never retry sends in a tight loop","Monitor for recurring 429s and adjust plan or rules"],"tags":["rate-limit","http-429","circuit-breaker","notifications"],"backgroundTag":"rate-limit-exceeded","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"}