gastownhall/beads · warning
transient error %d (attempt %d/%d)
Error message
transient error %d (attempt %d/%d)
What it means
The Azure DevOps client retry loop in doRequest hit a transient (retryable) HTTP status (e.g. 429, 5xx) and records this as lastErr on each failed attempt before backing off. The message includes the status code and attempt counter (attempt+1 of maxAttempts+1). It is usually surfaced wrapped by "max retries exceeded" (1408) rather than on its own; on its own it means the loop exited early via ctx cancellation. The client adds jittered exponential backoff, or honors a server-provided Retry-After delay when present.
Source
Thrown at internal/ado/client.go:274
retriable := resp.StatusCode == http.StatusTooManyRequests ||
resp.StatusCode >= 500
if retriable && attempt < maxAttempts {
delay := RetryDelay * time.Duration(1<<uint(attempt))
useServerDelay := false
if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
if seconds, parseErr := strconv.Atoi(retryAfter); parseErr == nil {
delay = time.Duration(seconds) * time.Second
useServerDelay = true
}
}
// Only add jitter to our own exponential backoff, not server-mandated delays
if !useServerDelay {
if half := int64(delay / 2); half > 0 {
delay += time.Duration(rand.Int64N(half)) //nolint:gosec // G404: jitter for retry backoff does not need crypto rand
}
}
lastErr = fmt.Errorf("transient error %d (attempt %d/%d)", resp.StatusCode, attempt+1, maxAttempts+1)
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-time.After(delay):
continue
}
}
return nil, &APIError{StatusCode: resp.StatusCode, Body: string(respBody)}
}
return nil, fmt.Errorf("max retries (%d) exceeded: %w", maxAttempts+1, lastErr)
}
// addAPIVersion appends the api-version query parameter to a URL string.
func addAPIVersion(urlStr string) string {
if strings.Contains(urlStr, "?") {
return urlStr + "&api-version=" + APIVersionView on GitHub (pinned to 71377f2769)
Solutions
- Retry the operation later with backoff; if you see it repeatedly, reduce request rate or batch IDs into fewer calls.
- Check the HTTP status code embedded in the message: 429 means rate limiting — slow down or request a limit increase; 5xx suggests a server-side issue.
- Verify your context/deadline isn't cutting retries short; increase the timeout so all retry attempts fit.
- Check Azure DevOps service status for ongoing incidents.
- Refresh/validate the PAT — 401/403-adjacent auth churn can masquerade as repeated failures.
Example fix
// before
for _, id := range all3000IDs { c.FetchWorkItems(ctx, idsChunk300) } // hammers API, 429s
// after
for i := 0; i < len(allIDs); i += 200 { c.FetchWorkItems(ctx, allIDs[i:i+200]); time.Sleep(500*time.Millisecond) } Defensive patterns
Strategy: retry
Validate before calling
// no pre-call check possible; optionally check ADO status endpoint
if rateLimitProbeFail() { time.Sleep(30 * time.Second) } Try / catch
_, err := client.FetchWorkItems(ctx, ids)
if err != nil {
var re *ado.RetryableError // or inspect wrapped status
if errors.As(err, &re) {
time.Sleep(backoffWithJitter(attempt))
// retry
}
} Prevention
- Throttle client-side request rate; batch work-item ID fetches.
- Honor Retry-After headers on 429 responses.
- Spread bulk jobs across time windows instead of bursting.
- Use a unique PAT per job to avoid shared quota exhaustion.
- Monitor for transient-error messages in logs to detect chronic rate limiting.
When it happens
Trigger: Any ADO REST call routed through doRequest (FetchWorkItems, fetchWorkItemsByWIQL, CreateWorkItem, UpdateWorkItem, AddWorkItemLink, RemoveWorkItemLink) receiving a retryable status on every attempt — typically 429 rate limiting or 500/502/503/504 from dev.azure.com.
Common situations: Bulk scripts hammering the WIQL/work-item API and tripping rate limits; shared PAT across many jobs exhausting org quotas; ADO service degradation/incidents; long batch operations where context deadline cancels mid-backoff.
Related errors
- max retries (%d) exceeded: %w
- transient error %d (attempt %d/%d)
- failed to list projects: %w
- failed to fetch work items: %w
- failed to add work item link: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a193249bef17c65d.
Report an issue: GitHub.