AlistGo/alist · error

task.Errstr

Error message

task.Errstr

What it means

checkCookieAsyncTask polls a 360 async operation (move/recycle return a task id). When the task reaches terminal status 10 with a non-zero Errno that is not tolerated, and the task carries an Errstr, that server message becomes this error (drivers/yunpan360/util.go:831). It means the async operation ultimately failed server-side.

Source

Thrown at drivers/yunpan360/util.go:831

		}
	}

	// Keep prior behavior when the async task is still pending after the probe window.
	return nil
}

func checkCookieAsyncTask(task CookieAsyncTask, toleratedErrnos map[int]struct{}) (bool, error) {
	if task.Status != 10 {
		return false, nil
	}
	if task.Errno == 0 {
		return true, nil
	}
	if _, ok := toleratedErrnos[task.Errno]; ok {
		return true, nil
	}
	if strings.TrimSpace(task.Errstr) != "" {
		return true, errors.New(task.Errstr)
	}
	if strings.TrimSpace(task.Action) != "" {
		return true, fmt.Errorf("yunpan async task %s failed: errno=%d", task.Action, task.Errno)
	}
	return true, fmt.Errorf("yunpan async task failed: errno=%d", task.Errno)
}

func (d *Yunpan360) openMove(ctx context.Context, srcName, dstPath string) error {
	signParams := map[string]string{
		"src_name": srcName,
		"new_name": dstPath,
	}
	return d.openPOST(ctx, "File.move", signParams, nil, signParams, nil, true)
}

func (d *Yunpan360) openDelete(ctx context.Context, targetPath string) error {
	return d.openPOST(ctx, "File.delete", nil, nil, map[string]string{
		"fname": targetPath,

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Re-list both source and destination to confirm current state, then retry the operation if still needed
  2. Read Errstr/errno — 'file not exists' means re-sync listings; session errors mean refresh the cookie
  3. For recurring races, serialize move/delete operations so concurrent UI actions don't invalidate tasks
  4. Retry once after refreshing the cookie if the errno indicates session expiry

Example fix

// before: treat any async error as fatal
if err := d.waitCookieAsyncTask(ctx, resp.Data.TaskID); err != nil { return err }

// after: tolerate benign 'already moved' errnos
if err := d.waitCookieAsyncTask(ctx, resp.Data.TaskID, toleratedErrnos{errnoAlreadyDone: {}}); err != nil { return err }
Defensive patterns

Strategy: retry

Type guard

func isAsyncTaskErr(err error) bool { return err != nil && (strings.Contains(err.Error(), "async task")) }

Try / catch

err := d.waitCookieAsyncTask(ctx, resp.Data.TaskID)
if err != nil && isTransientAsyncErr(err) {
    time.Sleep(backoff)
    err = d.waitCookieAsyncTask(ctx, resp.Data.TaskID)
}

Prevention

When it happens

Trigger: waitCookieAsyncTask polling a /file/move or /file/recycle task whose remote execution failed — e.g. source vanished mid-task, target folder deleted concurrently, quota exceeded, or session revoked while the task ran. Any cookie-mode move/recycle with resp.Data.IsAsync == true can end here.

Common situations: Large folder moves that race with user deletions in the 360 web UI; tasks abandoned across a cookie refresh; partial-failure errors from 360's backend after retries.

Related errors


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