{"record":{"id":"10545974217177f4","repo":"pulumi/pulumi","slug":"refresh-token-is-required","errorCode":null,"errorMessage":"refresh token is required","messagePattern":"refresh token is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/backend/httpstate/client/client.go","lineNumber":661,"sourceCode":"\tvar unmarshalledResp apitype.TokenExchangeGrantResponse\n\terr = json.Unmarshal(body, &unmarshalledResp)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &unmarshalledResp, nil\n}\n\n// RefreshAccessToken exchanges a Pulumi-issued refresh token for a fresh access token via\n// /api/oauth/token (grant_type=refresh_token, RFC 6749 §6). Returns the parsed token response;\n// the response's RefreshToken is the value to use on subsequent calls (the server may or may\n// not rotate it). The caller is responsible for writing the response's AccessToken back into\n// credentials.json when the exchange succeeds.\nfunc (pc *Client) RefreshAccessToken(\n\tctx context.Context,\n\trefreshToken string,\n) (*apitype.TokenExchangeGrantResponse, error) {\n\tif refreshToken == \"\" {\n\t\treturn nil, errors.New(\"refresh token is required\")\n\t}\n\ttokenURL := pc.apiURL + \"/api/oauth/token\"\n\tdata := url.Values{\n\t\t\"grant_type\":    {\"refresh_token\"},\n\t\t\"refresh_token\": {refreshToken},\n\t}\n\tbodyReader := strings.NewReader(data.Encode())\n\n\treq, err := http.NewRequestWithContext(ctx, \"POST\", tokenURL, bodyReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating HTTP request: %w\", err)\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/x-www-form-urlencoded\")\n\n\tresp, err := pc.restClient.HTTPClient().Do(req, retryAllMethods)\n\tif err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":643,"sourceCodeEnd":679,"githubUrl":"https://github.com/pulumi/pulumi/blob/793f7b2e160db4321fb7fb6b0607461e01cb251e/pkg/backend/httpstate/client/client.go#L643-L679","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nresp, _ := client.RefreshAccessToken(ctx, creds.RefreshToken) // may be \"\"\n// after\nif creds.RefreshToken == \"\" {\n    return errors.New(\"no refresh token stored; re-login required\")\n}\nresp, err := client.RefreshAccessToken(ctx, creds.RefreshToken)","handlingStrategy":"validation","validationCode":"if creds.RefreshToken == \"\" {\n    return errors.New(\"no refresh token stored; run `pulumi login` first\")\n}\n// safe to call RefreshAccessToken now","typeGuard":null,"tryCatchPattern":"resp, err := client.RefreshAccessToken(ctx, refreshToken)\nif err != nil {\n    if err.Error() == \"refresh token is required\" {\n        return fmt.Errorf(\"credentials missing refresh token: %w\", err)\n    }\n    return err\n}","preventionTips":["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"],"tags":["oauth","validation","refresh-token"],"backgroundTag":"missing-refresh-token","analyzedSha":"793f7b2e160db4321fb7fb6b0607461e01cb251e","analyzedAt":"2026-08-31T09:36:43.099Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}