src/Command.cpp:2967 calls exit(0) after freeing session state. quit is registered in the script command table (Command.h:99), so it is reachable from any host embedding the engine.
From Python:
import atexit, pyforefire
atexit.register(lambda: print("ATEXIT ran", flush=True))
f = pyforefire.ForeFire()
print("before quit[]", flush=True)
try:
f.execute("quit[]")
print("AFTER quit[]", flush=True)
finally:
print("FINALLY ran", flush=True)
Output is before quit[], and then the process is gone. Four problems in one result: the interpreter dies with no traceback or exception; finally, atexit, context managers and destructors never run; buffered output is lost, so without flush=True even the first print never appears (exit(0) flushes C streams, not Python's io layer); and it exits 0, so a batch job or CI step reports success.
It is not only user-triggered. Command.cpp:1125 calls quit() from the safe-topology error path, under // TODO supersafe mode ?. So an internal error can terminate the host process on its own, with exit code 0.
Suggested fix
Have quit release session state and return a status, and let the CLI front-end in app/forefire/ decide whether to exit. The path at line 1125 should propagate an error instead of terminating. If a hard abort must stay reachable, make it a distinct command that the Python binding does not expose.
Related to #159: quit is one of the few places that does delete session objects, so its ownership assumptions and the leak there should be settled together.
Drafted by Claude Opus 5 from a codebase audit. Reviewed by a maintainer before filing.
EDIT: rewrote for human readability.
src/Command.cpp:2967callsexit(0)after freeing session state.quitis registered in the script command table (Command.h:99), so it is reachable from any host embedding the engine.From Python:
Output is
before quit[], and then the process is gone. Four problems in one result: the interpreter dies with no traceback or exception;finally,atexit, context managers and destructors never run; buffered output is lost, so withoutflush=Trueeven the firstprintnever appears (exit(0)flushes C streams, not Python's io layer); and it exits 0, so a batch job or CI step reports success.It is not only user-triggered.
Command.cpp:1125callsquit()from the safe-topology error path, under// TODO supersafe mode ?. So an internal error can terminate the host process on its own, with exit code 0.Suggested fix
Have
quitrelease session state and return a status, and let the CLI front-end inapp/forefire/decide whether to exit. The path at line 1125 should propagate an error instead of terminating. If a hard abort must stay reachable, make it a distinct command that the Python binding does not expose.Related to #159:
quitis one of the few places that does delete session objects, so its ownership assumptions and the leak there should be settled together.Drafted by Claude Opus 5 from a codebase audit. Reviewed by a maintainer before filing.
EDIT: rewrote for human readability.