AlistGo/alist · warning

file already exists

Error message

file already exists

What it means

Returned by AferoAdapter.GetHandle (server/ftp/afero.go:102-104) when the FTP client's open flags include O_EXCL and a fs.Get for the target path already resolves. FTP semantics map this to an upload ('STOR') issued in exclusive-create mode: the client explicitly refuses to clobber an existing file, and the server honors that by erroring instead of overwriting.

Source

Thrown at server/ftp/afero.go:103

	a.nextFileSize = 0
	if (flags & os.O_SYNC) != 0 {
		return nil, errs.NotSupport
	}
	if (flags & os.O_APPEND) != 0 {
		return nil, errs.NotSupport
	}
	user := a.ctx.Value("user").(*model.User)
	path, err := user.JoinPath(name)
	if err != nil {
		return nil, err
	}
	_, err = fs.Get(a.ctx, path, &fs.GetArgs{})
	exists := err == nil
	if (flags&os.O_CREATE) == 0 && !exists {
		return nil, errs.ObjectNotFound
	}
	if (flags&os.O_EXCL) != 0 && exists {
		return nil, errors.New("file already exists")
	}
	if (flags & os.O_WRONLY) != 0 {
		if offset != 0 {
			return nil, errs.NotSupport
		}
		trunc := (flags & os.O_TRUNC) != 0
		if fileSize > 0 {
			return OpenUploadWithLength(a.ctx, path, trunc, fileSize)
		} else {
			return OpenUpload(a.ctx, path, trunc)
		}
	}
	return OpenDownload(a.ctx, path, offset)
}

func (a *AferoAdapter) SetNextFileSize(size int64) {
	a.nextFileSize = size
}

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Delete or rename the existing file server-side before re-uploading with exclusive flags
  2. Drop O_EXCL from the client's open flags and rely on O_TRUNC to overwrite
  3. Upload to a temp name and rename into place once complete

Example fix

// before (client flags)
flags := os.O_CREATE | os.O_WRONLY | os.O_EXCL
// after
flags := os.O_CREATE | os.O_WRONLY | os.O_TRUNC
Defensive patterns

Strategy: validation

Validate before calling

// Before exclusive-create upload, stat the target
if _, err := client.Stat(remotePath); err == nil {
    // decide: overwrite, rename, or abort — do not send O_EXCL
}

Try / catch

if err != nil && strings.Contains(err.Error(), "file already exists") {
    // client chose exclusive create; either delete first or switch to truncate mode
    _ = client.Delete(remotePath)
    err = uploadWithFlags(remotePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC)
}

Prevention

When it happens

Trigger: An FTP client that sends STOR with the 'exclusive create' / 'fail if exists' option (many libraries expose this as write-file-flags including O_EXCL, or curl/lftp with unique/rename-off semantics) targeting a path that already exists in the storage backend.

Common situations: Retrying an interrupted upload whose partial file already exists on the remote; scripts using O_EXCL|O_CREATE to make uploads atomic; sync tools that intentionally refuse overwrite and expect a specific error code.

Related errors


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