AlistGo/alist · error

%s : %s

Error message

%s : %s

What it means

Thrown by YandexDisk's token refresh in util.go when the OAuth token endpoint response parses into the error envelope (e.Error non-empty). The refresh_token grant failed, so no new AccessToken is obtained and every subsequent request would use stale credentials. Format is "<error> : <error_description>" straight from Yandex OAuth.

Source

Thrown at drivers/yandex_disk/util.go:30

)

// do others that not defined in Driver interface

func (d *YandexDisk) refreshToken() error {
	u := "https://oauth.yandex.com/token"
	var resp base.TokenResp
	var e TokenErrResp
	_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
		"grant_type":    "refresh_token",
		"refresh_token": d.RefreshToken,
		"client_id":     d.ClientID,
		"client_secret": d.ClientSecret,
	}).Post(u)
	if err != nil {
		return err
	}
	if e.Error != "" {
		return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
	}
	d.AccessToken, d.RefreshToken = resp.AccessToken, resp.RefreshToken
	op.MustSaveDriverStorage(d)
	return nil
}

func (d *YandexDisk) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
	u := "https://cloud-api.yandex.net/v1/disk/resources" + pathname
	req := base.RestyClient.R()
	req.SetHeader("Authorization", "OAuth "+d.AccessToken)
	if callback != nil {
		callback(req)
	}
	if resp != nil {
		req.SetResult(resp)
	}
	var e ErrResp
	req.SetError(&e)

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Re-run the OAuth authorization flow to obtain a fresh refresh token and save driver storage
  2. Verify ClientID and ClientSecret in driver config match the registered Yandex OAuth app
  3. Confirm RefreshToken is non-empty and was stored completely (no truncation)
  4. If error is invalid_grant due to rate limiting, wait and retry refresh rather than re-authorizing

Example fix

// before
if e.Error != "" {
    return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
}

// after
if e.Error != "" {
    if e.Error == "invalid_grant" {
        return fmt.Errorf("yandex disk: refresh token expired/revoked, re-authorization required: %s : %s", e.Error, e.ErrorDescription)
    }
    return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if strings.TrimSpace(d.RefreshToken) == "" || strings.TrimSpace(d.ClientID) == "" || strings.TrimSpace(d.ClientSecret) == "" {
    return errors.New("yandex disk: oauth config incomplete (refresh_token/client_id/client_secret)")
}

Try / catch

if err := d.refreshToken(); err != nil {
    if strings.Contains(err.Error(), "invalid_grant") {
        return fmt.Errorf("yandex disk: re-authorization required (refresh token revoked/expired): %w", err)
    }
    return err // transient (rate limit/network): retry later without re-auth
}

Prevention

When it happens

Trigger: Refresh token revoked or expired (Yandex refresh tokens can be invalidated after a year or by re-authorization); wrong client_id/client_secret pair; user changed account password; the stored RefreshToken is empty or was truncated.

Common situations: Long-lived deployment where nobody re-authorized for months; copying driver storage between instances causing token reuse; Yandex OAuth app settings changed (client secret rotated); rate limiting returning invalid_grant.

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/504ad790d31bf8ac. Report an issue: GitHub.