End-of-run cleanup SIGTERMs unrelated processes machine-wide (substring match on "python"/"torch"/"mp" cmdlines)
Related issues: no existing issue found mentioning psutil, the keyword sweep, or cross-run process kills (searched: psutil, kill, cleanup, parallel).
Summary
The final cleanup in launch_scientist_bfts.py first terminates the launcher's own child tree (correct), but then does an additional sweep over every process on the machine, killing any process whose command line contains one of the substrings "python", "torch", "mp", "bfts", "experiment". This kills processes that have nothing to do with the run — most damagingly, other concurrent AI-Scientist runs: when running multiple ideas in parallel on one machine, the first run to finish kills all the others mid-flight.
Where
launch_scientist_bfts.py, lines 321–369 at current main (96bd516). The problematic sweep is lines 347–359:
# Additional cleanup: find any orphaned processes containing specific keywords
keywords = ["python", "torch", "mp", "bfts", "experiment"]
for proc in psutil.process_iter(["name", "cmdline"]):
try:
# Check both process name and command line arguments
cmdline = " ".join(proc.cmdline()).lower()
if any(keyword in cmdline for keyword in keywords):
proc.send_signal(signal.SIGTERM)
proc.wait(timeout=3)
if proc.is_running():
proc.kill()
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.TimeoutExpired):
continueSpecific problems:
psutil.process_iteriterates all processes, not the run's descendants. The legitimate child cleanup has already happened just above (lines 326–345 collectcurrent_process.children(recursive=True)and terminate them), so this sweep only ever hits non-children.- Substring matching is extremely broad.
"python"matches every Python process owned by the user (other AI-Scientist launches, notebooks, editors' language servers, schedulers)."mp"matches any cmdline containing/tmp/,mpv,compute,import, etc. - The sweep matches the launcher process itself (its own cmdline contains
python ... launch_scientist_bfts.py), so the process SIGTERMs itself partway through the loop —sys.exit(0)at line 369 is never reached and the run exits with a signal status instead of0, which confuses any wrapper checking exit codes. - Only
AccessDeniedlimits the blast radius to same-user processes — which in practice is everything that matters on a shared workstation or a multi-run server.
Impact
Observed during batch reproduction (running multiple ideas in parallel on one machine, one launch_scientist_bfts.py per idea): the first launch to reach cleanup SIGTERMed the sibling launches and their experiment workers, aborting them mid-tree-search with no error of their own. Also observed launches terminating themselves via item 3, producing non-zero exit statuses for otherwise successful runs. We had to neutralize this block to run any parallel campaign.
Repro sketch
- On one machine, start two launches with different
--idea_idx(or any second long-running Python process). - Let the first reach the cleanup phase (e.g. a small/fast idea, or
--skip_writeup --skip_review). - Observe the second run (and any unrelated user Python process) receive SIGTERM; observe the first run's own exit status is a signal death rather than 0.
Suggested fix (minimal)
Delete the keyword sweep (lines 347–359). The preceding child-tree termination already handles the run's own processes, including recursive descendants.
If orphan cleanup beyond the child tree is genuinely needed (e.g. double-forked experiment processes), make it precise rather than keyword-based:
- launch experiment workers with
start_new_session=Trueand terminate the process group (os.killpg) on exit; or - record spawned PIDs at creation time and kill exactly those.
Either variant keeps cleanup scoped to processes the run actually created.
Source: SakanaAI/AI-Scientist-v2