AlistGo/alist · critical
failed to refresh token: refresh token is empty
Error message
failed to refresh token: refresh token is empty
What it means
Thrown by the legacy AliDrive (aliyundrive personal) token refresh when the open.aliyundrive.com OAuth token endpoint returns HTTP success with no error code but an EMPTY refresh_token in the response body. Because the driver rotates refresh tokens (each may be single-use), an empty new refresh token would brick the storage, so it aborts before overwriting stored credentials.
Source
Thrown at drivers/aliyundrive/util.go:77
func (d *AliDrive) refreshToken() error {
url := "https://auth.alipan.com/v2/account/token"
var resp base.TokenResp
var e RespErr
_, err := base.RestyClient.R().
//ForceContentType("application/json").
SetBody(base.Json{"refresh_token": d.RefreshToken, "grant_type": "refresh_token"}).
SetResult(&resp).
SetError(&e).
Post(url)
if err != nil {
return err
}
if e.Code != "" {
return fmt.Errorf("failed to refresh token: %s", e.Message)
}
if resp.RefreshToken == "" {
return errors.New("failed to refresh token: refresh token is empty")
}
d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
op.MustSaveDriverStorage(d)
return nil
}
func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error, RespErr) {
req := base.RestyClient.R()
state, ok := global.Load(d.UserID)
if !ok {
if url == "https://api.alipan.com/v2/user/get" {
state = &State{}
} else {
return nil, fmt.Errorf("can't load user state, user_id: %s", d.UserID), RespErr{}
}
}
req.SetHeaders(map[string]string{
"Authorization": "Bearer\t" + d.AccessToken,View on GitHub (pinned to 843d9dc814)
Solutions
- Ensure only ONE deployment/refresh-token holder uses this refresh token — concurrent refreshers consume each other's rotated tokens; disable the duplicate instance
- Re-obtain a fresh refresh_token (re-run the QR/token acquisition for the driver) and update the storage config
- Check e.Code/response body by enabling request logging to confirm whether the endpoint actually returned an error payload the code missed
- If the account was migrated to the Open Platform, switch to the aliyundrive_open driver instead of the legacy one
Example fix
// before
if resp.RefreshToken == "" {
return errors.New("failed to refresh token: refresh token is empty")
}
// after — surface the raw payload for diagnosis
if resp.RefreshToken == "" {
return fmt.Errorf("failed to refresh token: refresh token is empty, resp: %s", res.String())
} Defensive patterns
Strategy: validation
Validate before calling
// Before mounting, sanity-check the configured refresh token
if d.RefreshToken == "" {
return errors.New("refresh_token missing; re-authorize the storage")
}
// ensure no other instance refreshes with the same token
// (single owner per refresh token) Type guard
null
Try / catch
if err := d.refreshToken(); err != nil {
if strings.Contains(err.Error(), "refresh token is empty") {
// credentials are unusable: stop retrying and prompt re-authorization
}
return err
} Prevention
- Run exactly one refresher per refresh token — rotated tokens are single-use; shared tokens burn each other
- Store refresh tokens durably and only overwrite after verifying the replacement is non-empty
- Prefer the aliyundrive_open driver for new setups; the legacy API is deprecated
When it happens
Trigger: POST to the token endpoint with grant_type=refresh_token succeeds (no e.Code), but resp.RefreshToken == "" — server quirk, truncated body, rate limiting, or an account-side issue returning a partial payload.
Common situations: Long-running aliyundrive mounts whose stored refresh token was already consumed by another instance (two Alist deployments sharing one token); API endpoint behavior changes after alipan.com migration; network middleboxes mangling response bodies; token revoked from the Aliyun console.
Related errors
- not a jwt token because of invalid segments
- refresh token is empty
- failed to refresh token: %s
- refresh token failed: %s
- %s : %s
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/21e1c7ba34e2ea29.
Report an issue: GitHub.