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

  1. Verify free quota on the account and the existence of the destination directory, then retry the upload.
  2. Refresh the storage (re-Init) so jsToken and cookies are renewed, then retry.
  3. 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.
  4. 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

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.