bytebase/bytebase · error

exceeded max retries for %s %s

Error message

exceeded max retries for %s %s

What it means

doGraphRequest retries at most 3 times, and only when a response signals 401 plus a successful token refresh (retry=true). If all 3 attempts still yield retry signals, the loop exits and this sentinel error is returned. It means the Graph access token keeps being rejected even after refreshing, so the call never completes.

Source

Thrown at backend/plugin/webhook/teams/app.go:425

			}

			if resp.StatusCode < 200 || resp.StatusCode >= 300 {
				return nil, false, errors.Errorf("request failed with status %d: %s", resp.StatusCode, string(respBody))
			}

			return respBody, false, nil
		}()

		if err != nil {
			return nil, err
		}
		if retry {
			continue
		}
		return b, nil
	}

	return nil, errors.Errorf("exceeded max retries for %s %s", method, apiURL)
}

// Bot Framework types for messaging.

type activity struct {
	Type        string       `json:"type"`
	Text        string       `json:"text,omitempty"`
	Attachments []attachment `json:"attachments,omitempty"`
}

type attachment struct {
	ContentType string `json:"contentType"`
	Content     any    `json:"content"`
}

// AdaptiveCard represents a Microsoft Adaptive Card.
// Adaptive Card schema: https://adaptivecards.io/explorer/
// Adaptive Card designer: https://adaptivecards.io/designer/

View on GitHub (pinned to 1870550677)

Solutions

  1. Check refreshGraphToken requests scope "https://graph.microsoft.com/.default" and the token is a Graph token, not an Azure RM or Bot token.
  2. Verify admin consent is granted for the app's Graph permissions — tokens without consent get 401/403 repeatedly.
  3. Check server clock synchronization (NTP); large skew causes JWT rejection.
  4. Inspect the JWT claims (decode the access token at jwt.ms) to confirm aud/roles match the called endpoint.

Example fix

// before
return nil, errors.Errorf("exceeded max retries for %s %s", method, apiURL)
// after: include last status for diagnosability
return nil, errors.Errorf("exceeded max retries for %s %s (last status: %d)", method, apiURL, lastStatus)
Defensive patterns

Strategy: fallback

Validate before calling

// decode the access token and check claims before use
parts := strings.Split(graphToken, ".")
// verify aud == "https://graph.microsoft.com" and roles contain required Graph permissions

Try / catch

if err != nil {
	if strings.Contains(err.Error(), "exceeded max retries") {
		// token is persistently rejected: recheck scopes, consent, and clock sync
	}
	return err
}

Prevention

When it happens

Trigger: Three consecutive 401 responses from Graph API on the same URL even after refreshGraphToken succeeds each time — e.g. the token audience/resource claim is wrong, the app lacks consent so Graph rejects the token, or clock skew invalidating tokens.

Common situations: App registration configured for the wrong scope/audience (e.g. missing https://graph.microsoft.com/.default), Conditional Access or tenant policies rejecting service principal tokens, server clock drift breaking JWT validation.

Related errors


AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06). Data as JSON: /api/errors/0878232a459b4f35. Report an issue: GitHub.