{"record":{"id":"65b09e067995c25a","repo":"IceWhaleTech/CasaOS","slug":"refresh-token-is-empty","errorCode":null,"errorMessage":"refresh token is empty","messagePattern":"refresh token is empty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/onedrive/util.go","lineNumber":89,"sourceCode":"\tvar resp base.TokenResp\n\tvar e TokenErr\n\n\tres, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{\n\t\t\"grant_type\":    \"authorization_code\",\n\t\t\"client_id\":     d.ClientID,\n\t\t\"client_secret\": d.ClientSecret,\n\t\t\"code\":          d.Code,\n\t\t\"redirect_uri\":  d.RedirectUri,\n\t}).Post(url)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlogger.Info(\"get refresh token\", zap.String(\"res\", res.String()))\n\tif e.Error != \"\" {\n\t\treturn fmt.Errorf(\"%s\", e.ErrorDescription)\n\t}\n\tif resp.RefreshToken == \"\" {\n\t\treturn errors.New(\"refresh token is empty\")\n\t}\n\td.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken\n\treturn nil\n}\n\nfunc (d *Onedrive) _refreshToken() error {\n\turl := d.GetMetaUrl(true, \"\") + \"/common/oauth2/v2.0/token\"\n\tvar resp base.TokenResp\n\tvar e TokenErr\n\n\tres, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{\n\t\t\"grant_type\":    \"refresh_token\",\n\t\t\"client_id\":     d.ClientID,\n\t\t\"client_secret\": d.ClientSecret,\n\t\t\"redirect_uri\":  d.RedirectUri,\n\t\t\"refresh_token\": d.RefreshToken,\n\t}).Post(url)\n\tif err != nil {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/IceWhaleTech/CasaOS/blob/0d3b2f444ec0193193cf03eef6d43c6e35b0183e/drivers/onedrive/util.go#L71-L107","documentation":"During the OneDrive OAuth code exchange (grant_type=authorization_code), the token endpoint responded successfully but the parsed response contained an empty refresh_token field. The driver requires a refresh token for long-lived access, so it refuses to proceed. This typically means the authorization flow did not request the offline_access scope.","triggerScenarios":"Calling the initial token exchange with d.Code (the OAuth authorization code) when the authorize URL lacks the 'offline_access' scope, the code is invalid/expired, or the response was an error page that parsed to empty fields. Only triggered on first-time setup when RefreshToken is empty.","commonSituations":"Misconfigured AuthUrl missing 'offline_access' in the scope parameter; reusing an already-consumed authorization code; a tenant admin policy that strips refresh tokens; redirect_uri mismatch causing a soft-failed response.","solutions":["Ensure the authorize URL includes scope 'offline_access Files.ReadWrite.All' (offline_access is what makes the token endpoint issue a refresh_token).","Generate a fresh authorization code and re-run the exchange immediately — codes are single-use and short-lived (~10 minutes).","Verify redirect_uri in the token request exactly matches the one registered/used during authorization.","Inspect the logged response body (logger.Info \"get refresh token\") to see what the endpoint actually returned."],"exampleFix":"// before\nconfig.AuthUrl = \"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=\" + client_id + \"&response_type=code&redirect_uri=...&scope=files.readwrite.all&state=...\"\n\n// after\nconfig.AuthUrl = \"https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=\" + client_id + \"&response_type=code&redirect_uri=...&scope=offline_access+files.readwrite.all&state=...\"","handlingStrategy":"validation","validationCode":"// Before exchanging, verify the authorize flow used offline_access\nif !strings.Contains(authUrl, \"offline_access\") {\n\treturn errors.New(\"authorize URL must include offline_access scope\")\n}","typeGuard":null,"tryCatchPattern":"if err := d.GetRefreshToken(); err != nil {\n\tif strings.Contains(err.Error(), \"refresh token is empty\") {\n\t\t// re-run authorization flow with correct scope; do not retry exchange with same code\n\t}\n\treturn err\n}","preventionTips":["Always include offline_access in the OneDrive OAuth scope","Exchange authorization codes exactly once, immediately after consent","Log the full token response body when debugging OAuth setup"],"tags":["onedrive","oauth","refresh-token","authentication"],"backgroundTag":null,"analyzedSha":"0d3b2f444ec0193193cf03eef6d43c6e35b0183e","analyzedAt":"2026-08-15T13:27:57.821Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}