AlistGo/alist · error
lark refresh token response missing access token
Error message
lark refresh token response missing access token
What it means
The Lark refresh endpoint answered HTTP+code success but resp.Data was nil or AccessToken empty. The SDK call succeeded and resp.Success() passed, yet no usable credential came back — a malformed/unexpected body. The driver refuses to overwrite stored tokens with emptiness (which would lock the account out), so it aborts before the MustSaveDriverStorage in the lines below.
Source
Thrown at drivers/lark/driver.go:213
if c.RefreshTokenExpiresAt > 0 && time.Now().After(time.Unix(c.RefreshTokenExpiresAt, 0)) {
return "", errors.New("lark refresh token expired")
}
resp, err := c.client.Ext.Authen.RefreshAuthenAccessToken(ctx,
larkext.NewRefreshAuthenAccessTokenReqBuilder().
Body(larkext.NewRefreshAuthenAccessTokenReqBodyBuilder().
GrantType(larkext.GrantTypeRefreshCode).
RefreshToken(strings.TrimSpace(c.RefreshToken)).
Build()).
Build())
if err != nil {
return "", err
}
if !resp.Success() {
return "", errors.New(resp.Error())
}
if resp.Data == nil || resp.Data.AccessToken == "" {
return "", errors.New("lark refresh token response missing access token")
}
now := time.Now()
c.UserAccessToken = resp.Data.AccessToken
c.UserAccessTokenExpiresAt = now.Add(time.Duration(resp.Data.ExpiresIn) * time.Second).Unix()
if resp.Data.RefreshToken != "" {
c.RefreshToken = resp.Data.RefreshToken
}
if resp.Data.RefreshExpiresIn > 0 {
c.RefreshTokenExpiresAt = now.Add(time.Duration(resp.Data.RefreshExpiresIn) * time.Second).Unix()
}
op.MustSaveDriverStorage(c)
return c.UserAccessToken, nil
}
func (c *Lark) forceRefreshUserAccessToken(ctx context.Context) error {
if strings.TrimSpace(c.RefreshToken) == "" {View on GitHub (pinned to 843d9dc814)
Solutions
- Log resp.Error() and the raw response to see which envelope shape came back
- If the refresh token is dead, redo the OAuth authorization to mint a new pair
- Update alist / the vendored lark SDK in case the response schema changed
- Confirm the app still has the required drive scopes enabled in the Feishu console
Defensive patterns
Strategy: try-catch
Validate before calling
if strings.TrimSpace(c.RefreshToken) == "" {
return "", errors.New("cannot refresh: refresh_token empty")
} Try / catch
tok, err := c.getUserAccessToken(ctx, false)
if err != nil && strings.Contains(err.Error(), "missing access token") {
// success-shaped failure usually means dead refresh token: force re-auth, don't loop
return "", fmt.Errorf("lark token refresh unusable, re-authorize storage: %w", err)
} Prevention
- Never retry this error in a loop — the stored token pair is not updated on this path, so it repeats
- Log the raw refresh response once to distinguish revoked token from schema drift
When it happens
Trigger: RefreshAuthenAccessToken returns a 2xx whose data lacks access_token: expired/revoked refresh_token that Feishu answers with an odd envelope, gateway interference, or SDK deserialization drift after an API change.
Common situations: Refresh token actually dead but error surfaced as success-shaped; lark SDK version mismatch; tenant/app permissions changed so the endpoint no longer returns a token.
Related errors
- lark refresh token expired
- lark export task response missing ticket
- failed to get download link
- lark download requires web proxy
- lark tenant access token is empty
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/b70e84978e1470e1.
Report an issue: GitHub.