Background process cannot start if binary has been removed

17 views
Skip to first unread message

Paul Orasan

unread,
Aug 14, 2025, 3:51:31 PMAug 14
to PyInstaller
Hi! I've run into an issue where I get the following error:

```
"{binary_path} appears to have been moved or deleted since this application was launched. Continouation from this state is impossible. Exiting now."
```

Context:
- The application starts at a scheduled time;
- Upon an external event, the application starts a background process using the `multiprocessing` library
- In between the two, a deploy can alter the symlink to the binary by removing or updating it
- If the deploy has happened, the background process cannot start and dies with the error from above

From what I've read, the error is triggered when the background process tries to import python modules which it cannot find.
What I'm trying to understand is why that happens if the background process is started using the `fork` method? And then, as a follow-up, is there anything that can be done to mitigate this? 

The parent process works fine even though the binary has been altered. I suppose it is running off an temp directory? Could the background process also be started using it?

Code to reproduce:
```
import multiprocessing as mp
import sys, time, platform, pathlib
import faulthandler

EXE = pathlib.Path(sys.executable) # PyInstaller onefile
HB = pathlib.Path.cwd() / "heartbeat.log"
LOG = pathlib.Path.cwd() / "test.log"

def log(msg):
LOG.write_text((LOG.read_text() if LOG.exists() else "") + msg + "\n")

def worker():
log("worker: start")
for _ in range(20):
HB.write_text((HB.read_text() if HB.exists() else "") + msg + "\n")
time.sleep(0.5)
log("worker: done")

def try_delete_target(path: pathlib.Path):
try:
path.unlink()
log(f"deleter: successfully unlinked {path}")
except Exception as e:
log(f"deleter: failed to unlink {path}: {type(e).__name__}: {e}")

def spawn_worker(func):
mp.freeze_support()
log(f"parent: {mp.get_start_method()}")
p = mp.Process(target=func)
p.start()
return p

def main():
faulthandler.enable(open("faulthandler.txt", "w"))
LOG.write_text("")
HB.write_text("")
log(f"parent: exe path = {EXE}")
log(f"parent: platform = {platform.platform()}")

try_delete_target(EXE)
spawn_worker(worker)
log("parent: spawned background worker")

for i in range(6):
log(f"parent: tick {i}")
time.sleep(1)
log("parent: exiting")


if __name__ == "__main__":
main()
```

The platform I'm running this on is Linux. Would really appreciate some help and intuition on this one, thanks! :) 

bwoodsend

unread,
Aug 14, 2025, 5:18:01 PMAug 14
to PyInstaller

A onefile application runs in a temporary directory so tampering with the original executable would go unnoticed (although I wouldn’t call that guaranteed behaviour). That temporary directory would be inherited by the multiprocessing child but also be cleaned up when the original process exits. You'd really want your subprocess to re-extract and run in a fresh temporary directory so subprocess.run([sys.executable], env={**os.environ, "PYINSTALLER_RESET_ENVIRONMENT": "1"}) might do it (although this kind of application self-management/auto updating is something I wish people would just stop doing so I’m not feeling particularly minded to prove it out).

Paul Orasan

unread,
Aug 15, 2025, 3:54:08 AMAug 15
to pyins...@googlegroups.com
Thanks for the reply!

For my use case, the fact that the temp directory would be cleaned when the parent application exits would not be an issue because the child process lifespan is always shorter than the parent. 

However, what I see is that the child process doesn’t inherit the temp directory or at least not use it to search for the modules. I tried setting the reset environment variable but I still face the same exception. 

I should note that this application is not bundled as a onefile application but as onedir. If you can offer some further guidance on this, I would really appreciate it! Thanks.

În joi, 14 aug. 2025 la 23:18 bwoodsend <bwoo...@gmail.com> a scris:

A onefile application runs in a temporary directory so tampering with the original executable would go unnoticed (although I wouldn’t call that guaranteed behaviour). That temporary directory would be inherited by the multiprocessing child but also be cleaned up when the original process exits. You'd really want your subprocess to re-extract and run in a fresh temporary directory so subprocess.run([sys.executable], env={**os.environ, "PYINSTALLER_RESET_ENVIRONMENT": "1"}) might do it (although this kind of application self-management/auto updating is something I wish people would just stop doing so I’m not feeling particularly minded to prove it out).

--
You received this message because you are subscribed to a topic in the Google Groups "PyInstaller" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/pyinstaller/JpJALIrfqgw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to pyinstaller...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/pyinstaller/a43cb133-14bc-4e0b-8df0-9372fdd962ean%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages