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

  1. Retry the download — refreshToken runs before most operations, so a stale token often self-heals on the next attempt
  2. Verify the file still exists and is accessible via the SJTU netdisk web UI
  3. Re-authenticate the driver (update credentials in the AList storage config) to force a clean token
  4. 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

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


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/a146e4da2be97fc5. Report an issue: GitHub.