MemPalace/mempalace · critical · DaemonError
daemon exited during startup with code {proc.returncode}
Error message
daemon exited during startup with code {proc.returncode} What it means
DaemonError raised in start_daemon's readiness loop (mempalace/daemon.py:1345): the spawned daemon subprocess exited (proc.poll() is not None) before becoming healthy, with its exit code embedded in the message. The child is killed and reaped by the surrounding handler so no orphan holds the port/token/queue. Common underlying causes are visible in the daemon's log file.
Source
Thrown at mempalace/daemon.py:1345
if backend:
cmd.extend(["--backend", backend])
env = os.environ.copy()
if STATE_ROOT_ENV in os.environ:
env[STATE_ROOT_ENV] = os.environ[STATE_ROOT_ENV]
kwargs = _detached_kwargs(sd / "daemon.log")
proc = None
try:
proc = subprocess.Popen(cmd, env=env, **kwargs)
finally:
log_fh = kwargs.get("stdout")
if hasattr(log_fh, "close"):
log_fh.close()
try:
deadline = time.monotonic() + timeout
last_error = None
while time.monotonic() < deadline:
if proc.poll() is not None:
raise DaemonError(f"daemon exited during startup with code {proc.returncode}")
try:
client = DaemonClient(palace_path)
client.health()
return client
except DaemonError as exc:
last_error = exc
time.sleep(0.1)
raise DaemonError(f"daemon did not become ready: {last_error}")
except BaseException:
# Readiness failed — don't leak an orphaned detached child holding the
# port, token, queue, and log handle. Kill and reap it before raising.
if proc is not None and proc.poll() is None:
try:
proc.kill()
proc.wait()
except Exception: # noqa: BLE001 - cleanup best-effort
pass
raiseView on GitHub (pinned to 06cb6987f0)
Solutions
- Open the daemon log file (the path passed as stdout to Popen) and read the child's traceback — the exit code alone is secondary.
- Fix the root cause it shows (config validation error, port conflict, permissions, missing backend package).
- Verify no other daemon is already running (pid file, get_client_if_running) so the port is free.
- Retry start_daemon after fixing; the cleanup path guarantees no half-spawned child remains.
Example fix
# before
client = start_daemon(palace_path) # exits code 1, opaque
# after
try:
client = start_daemon(palace_path)
except DaemonError as exc:
print(open(daemon_log_path).read()) # child traceback explains the exit code Defensive patterns
Strategy: try-catch
Validate before calling
from mempalace.daemon import get_client_if_running
if get_client_if_running(palace_path) is None:
# validate config loads cleanly before spawning the daemon
from mempalace.config import load_config
load_config(palace_path) Try / catch
from mempalace.daemon import DaemonError
try:
client = start_daemon(palace_path)
except DaemonError as exc:
if "exited during startup" in str(exc):
log = Path(daemon_log).read_text() # child traceback explains the code
raise RuntimeError(f"daemon crash:\n{log}") from exc
raise Prevention
- Read the daemon log file immediately when startup fails — the exit code is not enough.
- Smoke-test config loading before spawning the daemon.
- Ensure the port is free and the backend package is installed before start_daemon.
When it happens
Trigger: Daemon process crashing at import/startup (bad config, missing dependency, unsupported backend); port already bound causing immediate exit; permission errors creating state files; a Python version mismatch in the spawned interpreter.
Common situations: First daemon start after a config edit that introduced error 200/202/203-class problems; upgrade left incompatible state; the log file handle (stdout redirect) contains the traceback; another daemon raced to bind the port.
Related errors
- daemon token not found for {palace_path}
- writable daemon startup refused: another writer owns local b
- daemon endpoint missing port
- daemon endpoint pid is not alive
- daemon did not become ready: {last_error}
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/3a67f376a8b922ac.
Report an issue: GitHub.