{"record":{"id":"09151465f5c53e99","repo":"AlistGo/alist","slug":"file-already-exists","errorCode":null,"errorMessage":"file already exists","messagePattern":"file already exists","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"server/ftp/afero.go","lineNumber":103,"sourceCode":"\ta.nextFileSize = 0\n\tif (flags & os.O_SYNC) != 0 {\n\t\treturn nil, errs.NotSupport\n\t}\n\tif (flags & os.O_APPEND) != 0 {\n\t\treturn nil, errs.NotSupport\n\t}\n\tuser := a.ctx.Value(\"user\").(*model.User)\n\tpath, err := user.JoinPath(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t_, err = fs.Get(a.ctx, path, &fs.GetArgs{})\n\texists := err == nil\n\tif (flags&os.O_CREATE) == 0 && !exists {\n\t\treturn nil, errs.ObjectNotFound\n\t}\n\tif (flags&os.O_EXCL) != 0 && exists {\n\t\treturn nil, errors.New(\"file already exists\")\n\t}\n\tif (flags & os.O_WRONLY) != 0 {\n\t\tif offset != 0 {\n\t\t\treturn nil, errs.NotSupport\n\t\t}\n\t\ttrunc := (flags & os.O_TRUNC) != 0\n\t\tif fileSize > 0 {\n\t\t\treturn OpenUploadWithLength(a.ctx, path, trunc, fileSize)\n\t\t} else {\n\t\t\treturn OpenUpload(a.ctx, path, trunc)\n\t\t}\n\t}\n\treturn OpenDownload(a.ctx, path, offset)\n}\n\nfunc (a *AferoAdapter) SetNextFileSize(size int64) {\n\ta.nextFileSize = size\n}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/AlistGo/alist/blob/843d9dc8149126976b2625911e45a4d3ffd6f2f5/server/ftp/afero.go#L85-L121","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (client flags)\nflags := os.O_CREATE | os.O_WRONLY | os.O_EXCL\n// after\nflags := os.O_CREATE | os.O_WRONLY | os.O_TRUNC","handlingStrategy":"validation","validationCode":"// Before exclusive-create upload, stat the target\nif _, err := client.Stat(remotePath); err == nil {\n    // decide: overwrite, rename, or abort — do not send O_EXCL\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"file already exists\") {\n    // client chose exclusive create; either delete first or switch to truncate mode\n    _ = client.Delete(remotePath)\n    err = uploadWithFlags(remotePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC)\n}","preventionTips":["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"],"tags":["ftp","upload","file-io","flags"],"backgroundTag":null,"analyzedSha":"843d9dc8149126976b2625911e45a4d3ffd6f2f5","analyzedAt":"2026-08-15T12:14:11.722Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}