pulumi/pulumi · warning
refresh token is required
Error message
refresh token is required
What it means
RefreshAccessToken requires a non-empty refresh token because the OAuth refresh_token grant cannot proceed without one. The client fails fast with this sentinel error before making any network call.
Source
Thrown at pkg/backend/httpstate/client/client.go:661
var unmarshalledResp apitype.TokenExchangeGrantResponse
err = json.Unmarshal(body, &unmarshalledResp)
if err != nil {
return nil, err
}
return &unmarshalledResp, nil
}
// RefreshAccessToken exchanges a Pulumi-issued refresh token for a fresh access token via
// /api/oauth/token (grant_type=refresh_token, RFC 6749 §6). Returns the parsed token response;
// the response's RefreshToken is the value to use on subsequent calls (the server may or may
// not rotate it). The caller is responsible for writing the response's AccessToken back into
// credentials.json when the exchange succeeds.
func (pc *Client) RefreshAccessToken(
ctx context.Context,
refreshToken string,
) (*apitype.TokenExchangeGrantResponse, error) {
if refreshToken == "" {
return nil, errors.New("refresh token is required")
}
tokenURL := pc.apiURL + "/api/oauth/token"
data := url.Values{
"grant_type": {"refresh_token"},
"refresh_token": {refreshToken},
}
bodyReader := strings.NewReader(data.Encode())
req, err := http.NewRequestWithContext(ctx, "POST", tokenURL, bodyReader)
if err != nil {
return nil, fmt.Errorf("creating HTTP request: %w", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err := pc.restClient.HTTPClient().Do(req, retryAllMethods)
if err != nil {
return nil, err
}View on GitHub (pinned to 793f7b2e16)
Solutions
- Ensure the refresh token is loaded from credentials.json (or your secret store) and is non-empty before calling RefreshAccessToken
- If no refresh token exists, re-run the login flow to obtain one instead of calling refresh
- Guard the call site: skip refresh and force re-login when the stored token is empty
- Check whether an earlier save/update of credentials accidentally cleared the refresh_token field
Example fix
// before
resp, _ := client.RefreshAccessToken(ctx, creds.RefreshToken) // may be ""
// after
if creds.RefreshToken == "" {
return errors.New("no refresh token stored; re-login required")
}
resp, err := client.RefreshAccessToken(ctx, creds.RefreshToken) Defensive patterns
Strategy: validation
Validate before calling
if creds.RefreshToken == "" {
return errors.New("no refresh token stored; run `pulumi login` first")
}
// safe to call RefreshAccessToken now Try / catch
resp, err := client.RefreshAccessToken(ctx, refreshToken)
if err != nil {
if err.Error() == "refresh token is required" {
return fmt.Errorf("credentials missing refresh token: %w", err)
}
return err
} Prevention
- Check credentials.json for a non-empty refresh_token after login
- Treat empty refresh token as 're-login required' in automation code
- Never clear refresh_token when rewriting credential files
- Cover the empty-token path in unit tests
When it happens
Trigger: Calling pc.RefreshAccessToken(ctx, "") — e.g. credentials.json lacking a refresh_token field, a variable that was never populated, or a code path assuming a token exists when the user authenticated via a different grant type.
Common situations: Users who logged in with an access-token-only flow and later attempt a programmatic refresh; config file edited/migrated losing the refresh_token field; tests passing an empty string.
Related errors
- refresh_token grant failed: %s: %s
- refresh_token grant returned empty access_token
- invalid empty stack name
- refusing to remove stack because it still contains resources
- a stack named %s already exists
AI-assisted analysis of pulumi/pulumi@793f7b2e16 (2026-08-31).
Data as JSON: /api/errors/10545974217177f4.
Report an issue: GitHub.