IceWhaleTech/CasaOS · error
refresh token is empty
Error message
refresh token is empty
What it means
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.
Source
Thrown at drivers/onedrive/util.go:89
var resp base.TokenResp
var e TokenErr
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
"grant_type": "authorization_code",
"client_id": d.ClientID,
"client_secret": d.ClientSecret,
"code": d.Code,
"redirect_uri": d.RedirectUri,
}).Post(url)
if err != nil {
return err
}
logger.Info("get refresh token", zap.String("res", res.String()))
if e.Error != "" {
return fmt.Errorf("%s", e.ErrorDescription)
}
if resp.RefreshToken == "" {
return errors.New("refresh token is empty")
}
d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
return nil
}
func (d *Onedrive) _refreshToken() error {
url := d.GetMetaUrl(true, "") + "/common/oauth2/v2.0/token"
var resp base.TokenResp
var e TokenErr
res, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
"grant_type": "refresh_token",
"client_id": d.ClientID,
"client_secret": d.ClientSecret,
"redirect_uri": d.RedirectUri,
"refresh_token": d.RefreshToken,
}).Post(url)
if err != nil {View on GitHub (pinned to 0d3b2f444e)
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.
Example fix
// before config.AuthUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=" + client_id + "&response_type=code&redirect_uri=...&scope=files.readwrite.all&state=..." // after config.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=..."
Defensive patterns
Strategy: validation
Validate before calling
// Before exchanging, verify the authorize flow used offline_access
if !strings.Contains(authUrl, "offline_access") {
return errors.New("authorize URL must include offline_access scope")
} Try / catch
if err := d.GetRefreshToken(); err != nil {
if strings.Contains(err.Error(), "refresh token is empty") {
// re-run authorization flow with correct scope; do not retry exchange with same code
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of IceWhaleTech/CasaOS@0d3b2f444e (2026-08-15).
Data as JSON: /api/errors/65b09e067995c25a.
Report an issue: GitHub.