AlistGo/alist · error
validate token failed: empty user sub
Error message
validate token failed: empty user sub
What it means
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.
Source
Thrown at drivers/guangyapan/driver.go:557
}
return d.refreshToken(ctx)
}
func (d *GuangYaPan) validateToken(ctx context.Context) error {
var me userMeResp
resp, err := d.accountClient.R().
SetContext(ctx).
SetHeader("Authorization", "Bearer "+d.AccessToken).
SetResult(&me).
Get("/v1/user/me")
if err != nil {
return err
}
if resp.IsError() {
return fmt.Errorf("validate token failed: status=%d body=%s", resp.StatusCode(), resp.String())
}
if strings.TrimSpace(me.Sub) == "" {
return errors.New("validate token failed: empty user sub")
}
return nil
}
func (d *GuangYaPan) refreshToken(ctx context.Context) error {
if strings.TrimSpace(d.RefreshToken) == "" {
return errors.New("refresh_token is empty")
}
var out tokenResp
resp, err := d.accountClient.R().
SetContext(ctx).
SetBody(map[string]any{
"client_id": d.ClientID,
"grant_type": "refresh_token",
"refresh_token": d.RefreshToken,
}).
SetResult(&out).View on GitHub (pinned to 843d9dc814)
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
Example fix
// before
if strings.TrimSpace(me.Sub) == "" {
return errors.New("validate token failed: empty user sub")
}
// after (keep the guard, add response context for diagnosis)
if strings.TrimSpace(me.Sub) == "" {
return fmt.Errorf("validate token failed: empty user sub, body=%s", resp.String())
} Defensive patterns
Strategy: retry
Validate before calling
if strings.TrimSpace(d.AccessToken) == "" {
if err := d.ensureAccessToken(ctx); err != nil { return err }
} Try / catch
err := d.validateToken(ctx)
if err != nil {
if strings.Contains(err.Error(), "empty user sub") {
// token maps to no identity: force refresh once, then re-login if it repeats
if rerr := d.refreshToken(ctx); rerr != nil { return rerr }
if err = d.validateToken(ctx); err != nil { return fmt.Errorf("revalidation failed: %w", err) }
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- login failed: provide a valid access_token, or refresh_token
- access token is empty
- refresh_token is empty
- get upload token failed: empty task id
- create offline task failed: empty task id
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/bc0fa51a3df33c31.
Report an issue: GitHub.