{"record":{"id":"eef6ff52c9a3375d","repo":"AlistGo/alist","slug":"validate-token-failed-status-d-body-s","errorCode":null,"errorMessage":"validate token failed: status=%d body=%s","messagePattern":"validate token failed: status=(.+?) body=(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/guangyapan/driver.go","lineNumber":554,"sourceCode":"\t\t\treturn errors.New(\"not logged in yet: please fill verify_code and save storage to finish SMS login\")\n\t\t}\n\t\treturn errors.New(\"access token is empty\")\n\t}\n\treturn d.refreshToken(ctx)\n}\n\nfunc (d *GuangYaPan) validateToken(ctx context.Context) error {\n\tvar me userMeResp\n\tresp, err := d.accountClient.R().\n\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\",","sourceCodeStart":536,"sourceCodeEnd":572,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/guangyapan/driver.go#L536-L572","documentation":"validateToken calls GET /v1/user/me on account.guangyapan.com with the current Bearer access token and throws this when the HTTP response is 4xx/5xx (resp.IsError()). It means the token was rejected or the account service errored, before the empty-sub check runs.","triggerScenarios":"Any flow that calls validateToken (storage init/refresh path) with an expired, revoked, or malformed AccessToken, or when the account API returns 5xx; status and raw body are embedded in the message.","commonSituations":"Access token expired and refresh token is also stale; the token was copied with extra whitespace or truncated; GuangYaPan account service outage or changed auth scheme.","solutions":["Read the embedded status: 401/403 means re-authenticate (refresh token or full SMS login); 5xx means retry later.","Trigger refreshToken (or re-run SMS login with phone + verify_code) to obtain fresh tokens.","If body shows invalid_client, confirm ClientID matches the expected default (aMe-8VSlkrbQXpUR) or the provider's current client id."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := d.validateToken(ctx); err != nil {\n    if re := d.RefreshToken; strings.TrimSpace(re) != \"\" {\n        if rerr := d.refreshToken(ctx); rerr == nil {\n            err = d.validateToken(ctx) // retry once with fresh token\n        }\n    }\n    if err != nil {\n        return fmt.Errorf(\"guangyapan auth needs full re-login: %w\", err)\n    }\n}","preventionTips":["Always store a refresh token alongside the access token.","Schedule periodic token validation before long batch jobs.","Treat repeated validation failures as a signal to redo SMS login."],"tags":["auth","access-token","http-error","guangyapan"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}