pulumi/pulumi · warning
esc: request rate-limit exceeded
Error message
esc: request rate-limit exceeded
What it means
The ESC client maps HTTP 429 (Too Many Requests) responses to this fixed error message. The Pulumi Cloud API rate-limits requests, and when the client's request exceeds that limit it surfaces this dedicated error instead of a generic server error.
Source
Thrown at pkg/cmd/esc/cli/client/client.go:1803
}
resp, err := doWithRetry(pc.httpClient, req, opts.RetryPolicy)
if err != nil {
// Don't wrap *apitype.ErrorResponse.
if _, ok := err.(*apitype.ErrorResponse); ok {
return nil, err
}
return nil, fmt.Errorf("performing HTTP request: %w", err)
}
// Provide a better error if using an authenticated call without having logged in first.
if resp.StatusCode == 401 && pc.apiToken == "" {
return nil, errors.New("this command requires logging in; try running `esc login` first")
}
// Provide a better error if rate-limit is exceeded(429: Too Many Requests)
if resp.StatusCode == 429 {
return nil, errors.New("esc: request rate-limit exceeded")
}
// For 4xx and 5xx failures, attempt to provide better diagnostics about what may have gone wrong.
if resp.StatusCode >= 400 && resp.StatusCode <= 599 {
// 4xx and 5xx responses should be of type ErrorResponse. See if we can unmarshal as that
// type, and if not just return the raw response text.
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("API call failed (%s), could not read response: %w", resp.Status, err)
}
reqID := ""
if resp.StatusCode >= 500 {
reqID = resp.Header.Get("X-Pulumi-Request-ID")
}
return nil, decodeError(respBody, resp.StatusCode, opts, reqID)
}
View on GitHub (pinned to 793f7b2e16)
Solutions
- Wait and retry the request later (respect the rate limit window)
- Add exponential backoff/jitter between API calls in scripts
- Batch or cache environment reads instead of calling the API per item
- Reduce concurrency in CI jobs that call ESC
Example fix
// before: tight loop
for env := range envs { client.GetEnvironment(ctx, env) }
// after: backoff
for env := range envs {
time.Sleep(backoff.Next())
client.GetEnvironment(ctx, env)
} Defensive patterns
Strategy: retry
Validate before calling
// Throttle client-side before hitting the API limiter := rate.NewLimiter(rate.Every(200*time.Millisecond), 1) _ = limiter.Wait(ctx) // before each esc client call
Try / catch
// Go: retry with backoff on rate-limit
var out *escenv.Environment
err := retry.Do(func() error {
env, err := client.GetEnvironment(ctx, org, proj, env)
if err != nil {
if strings.Contains(err.Error(), "rate-limit exceeded") {
return retry.DelayType(retry.BackOffDelay)(err) // or retryable marker
}
return retry.Unrecoverable(err)
}
out = env
return nil
}) Prevention
- Add backoff and jitter to all ESC API loops
- Batch environment reads/writes instead of per-item calls
- Limit parallelism in CI jobs that call ESC
When it happens
Trigger: Any esc client API call when the server returns status 429 — typically issuing many rapid ESC API requests (e.g. bulk environment reads/updates in scripts or CI loops).
Common situations: Automation scripts iterating over many environments; CI pipelines polling ESC; retry loops without backoff hammering the API; shared org hitting its quota.
Related errors
- rate limit exceeded: %w
- could not list bucket: %w
- failed to ping PostgreSQL: %w
- performing HTTP request: %w
- API call failed (%s), could not read response: %w
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/9266f25dbbcbb25e.
Report an issue: GitHub.