{"record":{"id":"48f77416cb867601","repo":"AlistGo/alist","slug":"missing-cookie-or-qrcode-account","errorCode":null,"errorMessage":"missing cookie or qrcode account","messagePattern":"missing cookie or qrcode account","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/115/util.go","lineNumber":75,"sourceCode":"\t}\n\td.client = driver115.New(opts...)\n\tcr := &driver115.Credential{}\n\tif d.QRCodeToken != \"\" {\n\t\ts := &driver115.QRCodeSession{\n\t\t\tUID: d.QRCodeToken,\n\t\t}\n\t\tif cr, err = d.client.QRCodeLoginWithApp(s, driver115.LoginApp(d.QRCodeSource)); err != nil {\n\t\t\treturn errors.Wrap(err, \"failed to login by qrcode\")\n\t\t}\n\t\td.Cookie = fmt.Sprintf(\"UID=%s;CID=%s;SEID=%s;KID=%s\", cr.UID, cr.CID, cr.SEID, cr.KID)\n\t\td.QRCodeToken = \"\"\n\t} else if d.Cookie != \"\" {\n\t\tif err = cr.FromCookie(d.Cookie); err != nil {\n\t\t\treturn errors.Wrap(err, \"failed to login by cookies\")\n\t\t}\n\t\td.client.ImportCredential(cr)\n\t} else {\n\t\treturn errors.New(\"missing cookie or qrcode account\")\n\t}\n\treturn d.client.LoginCheck()\n}\n\nfunc (d *Pan115) getFiles(fileId string) ([]FileObj, error) {\n\tres := make([]FileObj, 0)\n\tif d.PageSize <= 0 {\n\t\td.PageSize = driver115.FileListLimit\n\t}\n\tlimit := d.PageSize\n\tif limit > driver115.MaxDirPageLimit {\n\t\tlimit = driver115.MaxDirPageLimit\n\t}\n\n\topts := driver115.DefaultListOptions()\n\tdriver115.WithMultiUrls()(opts)\n\tif len(opts.ApiURLs) == 0 {\n\t\topts.ApiURLs = []string{driver115.ApiFileList}","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/115/util.go#L57-L93","documentation":"This is the error path in alist's custom ServeHTTP (internal/net/serve.go) for a plain, non-Range download. When the client sends no Range header (or the ranges were discarded), the server asks the storage driver for the whole file via RangeReadCloser.RangeRead(ctx, http_range.Range{Length: -1}); if the driver cannot produce a reader, the raw driver error text is written to the client with HTTP 416 (Requested Range Not Satisfiable), or 429 if the error is net.ErrExceedMaxConcurrency. The 416 status here is a code smell: it has nothing to do with ranges, it simply reuses the 'cannot open reader' status.","triggerScenarios":"Calling GET/HEAD on a proxied or streamed file (no 'Range: bytes=...' header) where the backend RangeReadCloser fails: storage driver link-fetch returns an error (expired token, 404 on the remote), the remote server is unreachable, credentials are invalid, or the download concurrency limit is exhausted so downloader.download() returns ErrExceedMaxConcurrency (internal/net/request.go:122) and the response becomes 429 Too Many Requests.","commonSituations":"Direct-link/proxy downloads from cloud drive drivers (Google Drive, OneDrive, etc.) after the cached link expires; misconfigured driver credentials after password change; reverse proxies with many parallel connections hitting the global concurrency limit; remote quota/rate-limit responses surfacing as raw error bodies; disk-driver failures (file deleted at the storage side).","solutions":["Read the response body: it contains the underlying driver error (err.Error()), which names the real cause (auth failure, 404, network timeout) — fix that first.","If the body says 'ExceedMaxConcurrency' (HTTP 429), reduce parallel connections/streams in the client or alist settings, or raise the concurrency limit for the driver/downloader.","Re-test with curl -v <url> without a Range header to confirm the failure is specific to full-file reads vs range reads.","If the error mentions expired links/tokens, refresh the driver token or clear the cached direct link and retry.","If the error persists across all files for one mount, verify the storage driver credentials and remote reachability in the alist admin UI."],"exampleFix":"// before: hammering the URL with many parallel full downloads\nfor i := 0; i < 20; i++ {\n    go http.Get(url) // some responses come back 429 ExceedMaxConcurrency\n}\n\n// after: limit concurrency to the server's budget and honor 429\nsem := make(chan struct{}, 4)\nfor i := 0; i < 20; i++ {\n    sem <- struct{}{}\n    go func() {\n        defer func() { <-sem }()\n        resp, err := http.Get(url)\n        if err == nil && resp.StatusCode == http.StatusTooManyRequests {\n            time.Sleep(time.Second * time.Duration(1+rand.Intn(3)))\n            // retry once\n        }\n    }()\n}","handlingStrategy":"retry","validationCode":"// Before downloading, verify the backend can produce a reader cheaply:\n// issue a 1-byte range probe; full-file reads share the same failure modes.\nreq, _ := http.NewRequest(\"GET\", fileURL, nil)\nreq.Header.Set(\"Range\", \"bytes=0-0\")\nresp, err := http.DefaultClient.Do(req)\nif err != nil || resp.StatusCode >= 400 {\n    // backend not ready / link expired / concurrency full: don't start the full download\n    return fmt.Errorf(\"backend not readable: status=%d\", resp.StatusCode)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat HTTP 416 on a NON-range request from alist as a backend failure, not a range problem — always log the body, it carries the driver error.","Honor 429 with backoff; it means the global download concurrency limit is exhausted.","Keep driver credentials fresh and clear cached direct links after token rotation.","Cap client-side parallel connections below alist's configured concurrency limit."],"tags":["go","http","alist","download","storage-driver","concurrency"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}