MemPalace/mempalace · error · DaemonError

daemon endpoint not found

Error message

daemon endpoint not found

What it means

DaemonError from _read_endpoint() (mempalace/daemon.py:182): the endpoint.json file under the palace state dir could not be opened or parsed. Both OSError (missing/unreadable file) and json.JSONDecodeError (truncated or corrupt JSON, e.g. after a crash mid-write) are chained into this error. Endpoint discovery is the first step of DaemonClient construction, so no daemon interaction can proceed.

Solutions

  1. If no daemon is meant to be running, start one via ensure_client/start_daemon instead of constructing DaemonClient directly.
  2. If a daemon should be running, check the daemon log for a startup crash, then delete the stale endpoint.json and restart.
  3. If endpoint.json is corrupt (truncated JSON), remove it and restart the daemon to rewrite it atomically.
  4. Verify state_dir(palace_path) points where you expect (canonical path, no symlinks/moves).

Example fix

# before
client = DaemonClient(palace_path)  # endpoint missing/corrupt

# after
client = get_client_if_running(palace_path)  # returns None instead of raising when absent
if client is None:
    client = start_daemon(palace_path)
Defensive patterns

Strategy: try-catch

Validate before calling

from mempalace import daemon

def endpoint_readable(palace_path: str) -> bool:
    p = daemon.endpoint_path(palace_path)
    try:
        json.loads(p.read_text(encoding="utf-8"))
        return True
    except (OSError, json.JSONDecodeError):
        return False

Try / catch

from mempalace.daemon import DaemonError, get_client_if_running

client = get_client_if_running(palace_path)  # returns None when absent/stale
if client is None:
    client = start_daemon(palace_path)

Prevention

When it happens

Trigger: Constructing DaemonClient or calling get_client_if_running when no daemon ever wrote endpoint.json; the daemon crashed while writing the endpoint file leaving partial JSON; the state dir was copied/moved without endpoint.json; reading permissions lost on the state dir.

Common situations: Hook runs on a fresh clone/machine before the first daemon start; a power loss or kill -9 during daemon startup corrupting endpoint.json; manual cleanup of state dirs; get_client_if_running normally converts this to None (daemon not running), so seeing it directly usually means a direct DaemonClient() call or a corrupted-file path.

Related errors


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/7ad433e3046f326e. Report an issue: GitHub.

Appendix: source

Thrown at mempalace/daemon.py:182

def endpoint_path(palace_path: str) -> Path:
    return state_dir(palace_path) / "endpoint.json"


def pid_path(palace_path: str) -> Path:
    return state_dir(palace_path) / "pid"


def queue_path(palace_path: str) -> Path:
    return state_dir(palace_path) / "queue.sqlite3"


def _read_endpoint(palace_path: str) -> dict[str, Any]:
    try:
        with open(endpoint_path(palace_path), encoding="utf-8") as fh:
            return json.load(fh)
    except (OSError, json.JSONDecodeError) as exc:
        raise DaemonError("daemon endpoint not found") from exc


def _pid_alive_windows(pid: int) -> bool:
    """Liveness probe for Windows that never sends a console control event.

    ``os.kill(pid, 0)`` is NOT a harmless existence check on Windows: signal 0
    is ``signal.CTRL_C_EVENT``, so Python routes it to
    ``GenerateConsoleCtrlEvent`` and sends a Ctrl-C to the target's process
    group instead of probing the pid. On a process with an attached console
    (e.g. a CI runner) that Ctrl-C is delivered back to *this* interpreter and
    surfaces as a spurious ``KeyboardInterrupt`` — exactly the hang seen when
    ``DaemonClient`` polled a same-process endpoint. Probe via the Win32 process
    handle API instead, which has no signalling side effects.
    """
    import ctypes
    from ctypes import wintypes

    SYNCHRONIZE = 0x00100000

View on GitHub (pinned to 06cb6987f0)