AlistGo/alist · error
123 offline download cannot target the root directory, pick
Error message
123 offline download cannot target the root directory, pick a sub directory
What it means
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.
Source
Thrown at drivers/123_open/offline.go:15
package _123Open
import (
"context"
"errors"
"fmt"
"github.com/alist-org/alist/v3/internal/model"
pan123 "github.com/okatu-loli/go-123pan"
)
// errOfflineRootDir is reported when an offline download is aimed at the root
// of the storage: the open platform rejects the root directory and silently
// drops such tasks into its own "来自:离线下载" folder instead.
var errOfflineRootDir = errors.New("123 offline download cannot target the root directory, pick a sub directory")
// OfflineDownload submits an offline download task that saves the URL into
// parentDir, and returns the task id to poll with OfflineProcess. It is used by
// the offline-download tool, which lives outside this package.
func (d *Open123) OfflineDownload(ctx context.Context, url string, parentDir model.Obj, fileName string) (int64, error) {
if err := d.ensureToken(ctx); err != nil {
return 0, err
}
dirID, err := parseFileID(parentDir.GetID())
if err != nil {
return 0, err
}
if dirID == 0 {
return 0, errOfflineRootDir
}
taskID, err := d.client.Offline.Download(ctx, &pan123.OfflineDownloadRequest{
URL: url,
FileName: fileName,View on GitHub (pinned to 843d9dc814)
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.
Example fix
// before
if precreateResp.Errno != 0 {
return fmt.Errorf("[terabox] failed to precreate file, errno: %d", precreateResp.Errno)
}
// after: retry once after refreshing session on auth errno
if precreateResp.Errno != 0 {
if isAuthErrno(precreateResp.Errno) {
if e := d.Init(ctx); e == nil {
return d.Put(ctx, dstDir, file, overwrite, session)
}
}
return fmt.Errorf("[terabox] failed to precreate file, errno: %d", precreateResp.Errno)
} Defensive patterns
Strategy: retry
Validate before calling
// verify target dir exists before upload
if _, err := d.getFiles(dstDir.GetPath()); err != nil {
// resolve destination first
} Type guard
func isTeraboxPrecreateErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "failed to precreate file")
} Try / catch
err := d.Put(ctx, dstDir, file, overwrite, session)
if isTeraboxPrecreateErr(err) {
if isAuthErrno(extractErrno(err)) {
_ = d.Init(ctx)
err = d.Put(ctx, dstDir, file, overwrite, session) // one retry
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/44b1364888789569.
Report an issue: GitHub.