AlistGo/alist · error
unexpected status code: %d
Error message
unexpected status code: %d
What it means
Thrown by the SJTU netdisk driver's Link (direct-link) method. It asks the API for a download URL and expects an HTTP 301 or 302 redirect; any other status (200 with a body, 401, 403, 5xx) triggers this error. It almost always means the API call itself misbehaved rather than the file being bad.
Source
Thrown at drivers/sjtu_netdisk/driver.go:166
resp, err := client.R().
SetContext(ctx).
SetQueryParams(map[string]string{
"access_token": d.accessToken,
"user_id": d.UserId,
"content_disposition": "attachment",
"purpose": "download",
"space_org_id": "1",
}).
Execute(http.MethodGet, linkURL)
if err != nil {
return nil, err
}
// status code is not 301 and 302
if resp.StatusCode() != http.StatusFound && resp.StatusCode() != http.StatusMovedPermanently {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
s3URL := resp.Header().Get("Location")
if s3URL == "" {
return nil, fmt.Errorf("no Location header in redirect response")
}
// parse TTL from S3 presigned URL's X-Amz-Expires parameter
ttl := 2 * time.Hour
if u, parseErr := url.Parse(s3URL); parseErr == nil {
if expiresSec, convErr := strconv.Atoi(u.Query().Get("X-Amz-Expires")); convErr == nil && expiresSec > 0 {
ttl = time.Duration(expiresSec) * time.Second
}
}
return &model.Link{
URL: s3URL,
Expiration: &ttl,View on GitHub (pinned to 843d9dc814)
Solutions
- Retry the download — refreshToken runs before most operations, so a stale token often self-heals on the next attempt
- Verify the file still exists and is accessible via the SJTU netdisk web UI
- Re-authenticate the driver (update credentials in the AList storage config) to force a clean token
- Check whether the API now returns the S3 URL in the response body instead of a Location redirect, and adapt the driver if so
Example fix
// before
if resp.StatusCode() != http.StatusFound && resp.StatusCode() != http.StatusMovedPermanently {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode())
}
// after: include response body to expose the API's error detail
if resp.StatusCode() != http.StatusFound && resp.StatusCode() != http.StatusMovedPermanently {
return nil, fmt.Errorf("unexpected status code: %d, body: %s", resp.StatusCode(), resp.String())
} Defensive patterns
Strategy: retry
Try / catch
if _, err := driver.Link(ctx, obj, model.LinkArgs{}); err != nil {
if strings.Contains(err.Error(), "unexpected status code") {
time.Sleep(2 * time.Second) // let token refresh settle
return driver.Link(ctx, obj, model.LinkArgs{})
}
return nil, err
} Prevention
- Ensure the jAccount token is refreshed before link operations
- Verify file existence before requesting direct links
- Wrap link fetching with one retry for transient 401/5xx
When it happens
Trigger: Calling the download-link endpoint with an expired access_token (401), a file ID that was deleted (404), insufficient permission on the space, or an API change that now returns 200 with the URL in the body instead of a redirect.
Common situations: Token refresh raced or failed before the link request; file was removed in the SJTU web UI while cached in AList; jAccount account lacks permission on the shared org space (space_org_id=1); upstream API contract changed after a service update.
Related errors
- stopped after 10 redirects
- no Location header in redirect response
- missing cookie or qrcode account
- invalid range: failed to overlap
- not found 302 redirect
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/a146e4da2be97fc5.
Report an issue: GitHub.