{"record":{"id":"44b1364888789569","repo":"AlistGo/alist","slug":"123-offline-download-cannot-target-the-root-direct","errorCode":null,"errorMessage":"123 offline download cannot target the root directory, pick a sub directory","messagePattern":"123 offline download cannot target the root directory, pick a sub directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"drivers/123_open/offline.go","lineNumber":15,"sourceCode":"package _123Open\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/alist-org/alist/v3/internal/model\"\n\tpan123 \"github.com/okatu-loli/go-123pan\"\n)\n\n// errOfflineRootDir is reported when an offline download is aimed at the root\n// of the storage: the open platform rejects the root directory and silently\n// drops such tasks into its own \"来自:离线下载\" folder instead.\nvar errOfflineRootDir = errors.New(\"123 offline download cannot target the root directory, pick a sub directory\")\n\n// OfflineDownload submits an offline download task that saves the URL into\n// parentDir, and returns the task id to poll with OfflineProcess. It is used by\n// the offline-download tool, which lives outside this package.\nfunc (d *Open123) OfflineDownload(ctx context.Context, url string, parentDir model.Obj, fileName string) (int64, error) {\n\tif err := d.ensureToken(ctx); err != nil {\n\t\treturn 0, err\n\t}\n\tdirID, err := parseFileID(parentDir.GetID())\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif dirID == 0 {\n\t\treturn 0, errOfflineRootDir\n\t}\n\ttaskID, err := d.client.Offline.Download(ctx, &pan123.OfflineDownloadRequest{\n\t\tURL:      url,\n\t\tFileName: fileName,","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/drivers/123_open/offline.go#L1-L33","documentation":"Returned by Terabox.Put (drivers/terabox/driver.go:174) when the /api/precreate step of the chunked upload reports a non-zero errno. Precreate reserves the upload (uploadid, block list); failing here aborts the upload before any chunk transfer.","triggerScenarios":"Uploading to a target path that does not exist or is a file; quota exceeded; invalid block_list or size parameters (e.g. mismatch between stream size and slice md5 list); expired jsToken or session mid-upload; uploading while the account is rate-limited.","commonSituations":"Insufficient Terabox storage quota; target directory renamed or deleted concurrently; large files where temp-file caching and md5 block computation produced an inconsistent block_list; cookie/jsToken stale after long server uptime; concurrent uploads from several instances.","solutions":["Verify free quota on the account and the existence of the destination directory, then retry the upload.","Refresh the storage (re-Init) so jsToken and cookies are renewed, then retry.","Log precreateResp fields and the raw response (the code already debug-logs res) to map errno to the documented Terabox error table and fix the specific cause.","Reduce concurrency or retry with backoff if errno indicates rate limiting."],"exampleFix":"// before\nif precreateResp.Errno != 0 {\n\treturn fmt.Errorf(\"[terabox] failed to precreate file, errno: %d\", precreateResp.Errno)\n}\n\n// after: retry once after refreshing session on auth errno\nif precreateResp.Errno != 0 {\n\tif isAuthErrno(precreateResp.Errno) {\n\t\tif e := d.Init(ctx); e == nil {\n\t\t\treturn d.Put(ctx, dstDir, file, overwrite, session)\n\t\t}\n\t}\n\treturn fmt.Errorf(\"[terabox] failed to precreate file, errno: %d\", precreateResp.Errno)\n}","handlingStrategy":"retry","validationCode":"// verify target dir exists before upload\nif _, err := d.getFiles(dstDir.GetPath()); err != nil {\n\t// resolve destination first\n}","typeGuard":"func isTeraboxPrecreateErr(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"failed to precreate file\")\n}","tryCatchPattern":"err := d.Put(ctx, dstDir, file, overwrite, session)\nif isTeraboxPrecreateErr(err) {\n\tif isAuthErrno(extractErrno(err)) {\n\t\t_ = d.Init(ctx)\n\t\terr = d.Put(ctx, dstDir, file, overwrite, session) // one retry\n\t}\n}","preventionTips":["Check quota and destination validity before large uploads.","Refresh the storage session before long-running upload batches.","Keep the source stream immutable during upload so md5 block lists stay consistent."],"tags":["terabox","upload","api-error","quota"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}