{"record":{"id":"504ad790d31bf8ac","repo":"AlistGo/alist","slug":"s-s-504ad7","errorCode":null,"errorMessage":"%s : %s","messagePattern":"%s : %s","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/yandex_disk/util.go","lineNumber":30,"sourceCode":")\n\n// do others that not defined in Driver interface\n\nfunc (d *YandexDisk) refreshToken() error {\n\tu := \"https://oauth.yandex.com/token\"\n\tvar resp base.TokenResp\n\tvar e TokenErrResp\n\t_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{\n\t\t\"grant_type\":    \"refresh_token\",\n\t\t\"refresh_token\": d.RefreshToken,\n\t\t\"client_id\":     d.ClientID,\n\t\t\"client_secret\": d.ClientSecret,\n\t}).Post(u)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif e.Error != \"\" {\n\t\treturn fmt.Errorf(\"%s : %s\", e.Error, e.ErrorDescription)\n\t}\n\td.AccessToken, d.RefreshToken = resp.AccessToken, resp.RefreshToken\n\top.MustSaveDriverStorage(d)\n\treturn nil\n}\n\nfunc (d *YandexDisk) request(pathname string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {\n\tu := \"https://cloud-api.yandex.net/v1/disk/resources\" + pathname\n\treq := base.RestyClient.R()\n\treq.SetHeader(\"Authorization\", \"OAuth \"+d.AccessToken)\n\tif callback != nil {\n\t\tcallback(req)\n\t}\n\tif resp != nil {\n\t\treq.SetResult(resp)\n\t}\n\tvar e ErrResp\n\treq.SetError(&e)","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/yandex_disk/util.go#L12-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the OAuth authorization flow to obtain a fresh refresh token and save driver storage","Verify ClientID and ClientSecret in driver config match the registered Yandex OAuth app","Confirm RefreshToken is non-empty and was stored completely (no truncation)","If error is invalid_grant due to rate limiting, wait and retry refresh rather than re-authorizing"],"exampleFix":"// before\nif e.Error != \"\" {\n    return fmt.Errorf(\"%s : %s\", e.Error, e.ErrorDescription)\n}\n\n// after\nif e.Error != \"\" {\n    if e.Error == \"invalid_grant\" {\n        return fmt.Errorf(\"yandex disk: refresh token expired/revoked, re-authorization required: %s : %s\", e.Error, e.ErrorDescription)\n    }\n    return fmt.Errorf(\"%s : %s\", e.Error, e.ErrorDescription)\n}","handlingStrategy":"try-catch","validationCode":"if strings.TrimSpace(d.RefreshToken) == \"\" || strings.TrimSpace(d.ClientID) == \"\" || strings.TrimSpace(d.ClientSecret) == \"\" {\n    return errors.New(\"yandex disk: oauth config incomplete (refresh_token/client_id/client_secret)\")\n}","typeGuard":null,"tryCatchPattern":"if err := d.refreshToken(); err != nil {\n    if strings.Contains(err.Error(), \"invalid_grant\") {\n        return fmt.Errorf(\"yandex disk: re-authorization required (refresh token revoked/expired): %w\", err)\n    }\n    return err // transient (rate limit/network): retry later without re-auth\n}","preventionTips":["Surface invalid_grant as 're-login required' instead of a generic error","Persist the rotated RefreshToken immediately after each refresh (the code already does MustSaveDriverStorage)","Never copy driver storage between instances — refresh tokens are single-use in rotation"],"tags":["yandex-disk","oauth","refresh-token","auth","go"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}