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

  1. Wait and retry the request later (respect the rate limit window)
  2. Add exponential backoff/jitter between API calls in scripts
  3. Batch or cache environment reads instead of calling the API per item
  4. 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

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


AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31). Data as JSON: /api/errors/9266f25dbbcbb25e. Report an issue: GitHub.