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
- Delete or rename the existing file server-side before re-uploading with exclusive flags
- Drop O_EXCL from the client's open flags and rely on O_TRUNC to overwrite
- 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
- Default upload scripts to O_TRUNC unless exclusivity is required
- For atomic uploads, write to a temp name and rename into place
- Clear stale partial files after interrupted transfers before retrying
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
- 123 offline download cannot target the root directory, pick
- file_id is required
- same-name dirs cannot be Put
- invalid hash
- empty files are not allowed by baidu netdisk
AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15).
Data as JSON: /api/errors/09151465f5c53e99.
Report an issue: GitHub.