Skip to content

xftp: recreate work directory if missing before creating transfer prefix path#1823

Open
Narasimha-sc wants to merge 2 commits into
simplex-chat:masterfrom
Narasimha-sc:fix-xftp-workdir-missing
Open

xftp: recreate work directory if missing before creating transfer prefix path#1823
Narasimha-sc wants to merge 2 commits into
simplex-chat:masterfrom
Narasimha-sc:fix-xftp-workdir-missing

Conversation

@Narasimha-sc

@Narasimha-sc Narasimha-sc commented Jul 2, 2026

Copy link
Copy Markdown

Problem

xftpSendFile', xftpSendDescription' and xftpReceiveFile' create their per-transfer work directory with a non-recursive createDirectory on the path from getPrefixPath, which assumes the configured XFTP work directory already exists. If that directory is removed while the app is running (third-party temp/disk cleaner, roaming-profile sync, or manual deletion), every subsequent transfer fails and cannot recover until the app is restarted.

Reported from simplex-chat desktop (Windows):

CreateDirectory "\\?\C:\Users\<user>\AppData\Roaming\SimpleX\tmp\20260701_231208_256584_snd.xftp": does not exist

...\Roaming\SimpleX\tmp is the work directory; it is created once at startup but never re-created before a transfer.

Root cause

src/Simplex/FileTransfer/Agent.hs:

getPrefixPath suffix = do
  workPath <- getXFTPWorkPath           -- not guaranteed to still exist
  ...
  uniqueCombine workPath (isoTime <> "_" <> suffix)

-- callers:
prefixPath <- lift $ getPrefixPath "snd.xftp"
createDirectory prefixPath              -- non-recursive; throws if workPath is gone

Fix

Ensure the work directory exists in getPrefixPath with createDirectoryIfMissing True. This is a single point that covers all three transfer entry points (send, send-description, receive) and the chunk/tmp paths built from getXFTPWorkPath via toFSFilePath. The individual createDirectory prefixPath calls are unchanged — uniqueCombine still yields a fresh name.

getPrefixPath suffix = do
  workPath <- getXFTPWorkPath
  createDirectoryIfMissing True workPath
  ...

Testing

Added a regression test (should recreate work directory removed while running before sending) that removes the agent work directory after workers start, then sends a file.

Verified red→green locally (GHC 9.6.3), running only the new test:

  • Without the fix: 1 example, 1 failurecreateDirectory: does not exist on …/work/<ts>_snd.xftp (reproduces the reported error).
  • With the fix: 1 example, 0 failures.

The full /XFTP/XFTP agent/ group also passes with the fix (27 examples, 0 failures, 1 pending), confirming no regression to normal transfers.

Notes

  • createDirectoryIfMissing is already in scope (import UnliftIO.Directory).
  • Non-recursive createDirectory calls that build paths inside an already-created prefixPath are intentionally left as-is; their parent exists by construction.

…fix path

xftpSendFile', xftpSendDescription' and xftpReceiveFile' create their per-transfer
work directory with a non-recursive createDirectory on the path returned by
getPrefixPath, which assumes the configured XFTP work directory already exists. If
that directory is removed while the app is running (e.g. a temp/disk cleaner, profile
sync or manual deletion), every subsequent transfer fails with
"CreateDirectory ...: does not exist" and cannot recover until restart.

Ensure the work directory exists in getPrefixPath via createDirectoryIfMissing True,
covering all three entry points (and the chunk/tmp paths built from getXFTPWorkPath).
Sends a file after removing the agent work directory (simulating a temp/disk
cleaner, roaming-profile sync or manual deletion). Fails without the getPrefixPath
fix with "createDirectory ...: does not exist", passes with it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant