{"record":{"id":"346f0f651e1bcfb2","repo":"AlistGo/alist","slug":"refresh-token-is-empty","errorCode":null,"errorMessage":"refresh_token is empty","messagePattern":"refresh_token is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/guangyapan/driver.go","lineNumber":564,"sourceCode":"\t\tSetContext(ctx).\n\t\tSetHeader(\"Authorization\", \"Bearer \"+d.AccessToken).\n\t\tSetResult(&me).\n\t\tGet(\"/v1/user/me\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resp.IsError() {\n\t\treturn fmt.Errorf(\"validate token failed: status=%d body=%s\", resp.StatusCode(), resp.String())\n\t}\n\tif strings.TrimSpace(me.Sub) == \"\" {\n\t\treturn errors.New(\"validate token failed: empty user sub\")\n\t}\n\treturn nil\n}\n\nfunc (d *GuangYaPan) refreshToken(ctx context.Context) error {\n\tif strings.TrimSpace(d.RefreshToken) == \"\" {\n\t\treturn errors.New(\"refresh_token is empty\")\n\t}\n\n\tvar out tokenResp\n\tresp, err := d.accountClient.R().\n\t\tSetContext(ctx).\n\t\tSetBody(map[string]any{\n\t\t\t\"client_id\":     d.ClientID,\n\t\t\t\"grant_type\":    \"refresh_token\",\n\t\t\t\"refresh_token\": d.RefreshToken,\n\t\t}).\n\t\tSetResult(&out).\n\t\tPost(\"/v1/auth/token\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif resp.IsError() || out.Error != \"\" || strings.TrimSpace(out.AccessToken) == \"\" {\n\t\terrMsg := strings.TrimSpace(out.ErrorDesc)\n\t\tif errMsg == \"\" {","sourceCodeStart":546,"sourceCodeEnd":582,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/guangyapan/driver.go#L546-L582","documentation":"refreshToken refuses to run when driver.storage has no refresh_token. It is a hard precondition: the OAuth-style refresh grant (POST /v1/auth/token with grant_type=refresh_token) is impossible without it, and the driver deliberately fails fast instead of sending a doomed request. Usually surfaced after ensureAccessToken found the access token empty and delegated here.","triggerScenarios":"ensureAccessToken reaches refreshToken because access_token is blank but refresh_token is also blank (race with the SMS branch above), or postAPI got a 401/403 and called refreshToken on a storage whose refresh_token field was cleared. Condition: strings.TrimSpace(d.RefreshToken)==\"\".","commonSituations":"Token pair was half-deleted during manual storage editing; provider invalidated and the driver previously overwrote RefreshToken with an empty value; copy-paste of storage config dropped the refresh_token key.","solutions":["Restore or re-enter a valid refresh_token in the GuangYaPan storage config, then retry the operation","If the refresh token was revoked server-side, redo the full SMS login to obtain a new token pair","Check for code paths or scripts that save driver storage with an empty RefreshToken field and fix them"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if strings.TrimSpace(d.RefreshToken) == \"\" {\n\treturn errors.New(\"cannot refresh guangyapan token: refresh_token missing from storage\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Persist refresh tokens atomically: never write storage with an empty refresh_token over a populated one","When migrating storage between instances, copy the whole token pair together"],"tags":["guangyapan","authentication","config","driver"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}