{"record":{"id":"bc0fa51a3df33c31","repo":"AlistGo/alist","slug":"validate-token-failed-empty-user-sub","errorCode":null,"errorMessage":"validate token failed: empty user sub","messagePattern":"validate token failed: empty user sub","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/guangyapan/driver.go","lineNumber":557,"sourceCode":"\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\",\n\t\t\t\"refresh_token\": d.RefreshToken,\n\t\t}).\n\t\tSetResult(&out).","sourceCodeStart":539,"sourceCodeEnd":575,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/guangyapan/driver.go#L539-L575","documentation":"GuangYaPan.validateToken calls GET /v1/user/me with the stored bearer token; the HTTP call succeeded (non-error status) but the JSON field userMeResp.Sub came back empty. The server accepted the request yet returned a payload without a user subject, so the token cannot be trusted for identity. This is a contract violation from the account API, not a network failure.","triggerScenarios":"GET /v1/user/me returns 2xx with a body where the \"sub\" claim is missing or blank (e.g. token maps to a deleted account, an A/B response shape change on the account endpoint, or a proxy returning an empty 200). Triggers on strings.TrimSpace(me.Sub)==\"\" after resp.IsError() is false.","commonSituations":"GuangYaPan backend changed its /v1/user/me response schema; account deactivated server-side; token partially valid (accepted but identity stripped); an intermediate gateway rewriting responses.","solutions":["Log resp.String() alongside the error to inspect the actual /v1/user/me payload and confirm the field name/shape","Force a token refresh: clear access_token in storage so ensureAccessToken calls refreshToken, then retry validation","Re-login from scratch (SMS flow) to mint a fresh token in case the account state is stale","If the payload shape changed, update userMeResp in drivers/guangyapan/types.go to match the new schema"],"exampleFix":"// before\nif strings.TrimSpace(me.Sub) == \"\" {\n\treturn errors.New(\"validate token failed: empty user sub\")\n}\n// after (keep the guard, add response context for diagnosis)\nif strings.TrimSpace(me.Sub) == \"\" {\n\treturn fmt.Errorf(\"validate token failed: empty user sub, body=%s\", resp.String())\n}","handlingStrategy":"retry","validationCode":"if strings.TrimSpace(d.AccessToken) == \"\" {\n\tif err := d.ensureAccessToken(ctx); err != nil { return err }\n}","typeGuard":null,"tryCatchPattern":"err := d.validateToken(ctx)\nif err != nil {\n\tif strings.Contains(err.Error(), \"empty user sub\") {\n\t\t// token maps to no identity: force refresh once, then re-login if it repeats\n\t\tif rerr := d.refreshToken(ctx); rerr != nil { return rerr }\n\t\tif err = d.validateToken(ctx); err != nil { return fmt.Errorf(\"revalidation failed: %w\", err) }\n\t}\n}","preventionTips":["Run validateToken on storage Init so bad identity tokens fail at mount time, not mid-operation","Log the /v1/user/me body on failure once to distinguish schema drift from account state"],"tags":["guangyapan","authentication","api-contract","driver"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}