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

  1. Ensure the refresh token is loaded from credentials.json (or your secret store) and is non-empty before calling RefreshAccessToken
  2. If no refresh token exists, re-run the login flow to obtain one instead of calling refresh
  3. Guard the call site: skip refresh and force re-login when the stored token is empty
  4. 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

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


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