rohitg00/agentmemory · warning

EADDRINUSE

EADDRINUSE

Error message

[agentmemory] Viewer port ${requestedPort} is in use while bound to ${host}; not retrying because non-loopback viewer binds require VIEWER_ALLOWED_HOSTS to match the exact port. Free the port, choose another viewer port, or unset AGENTMEMORY_VIEWER_HOST to keep the safe loopback bind.

What it means

When the viewer HTTP server fails to bind, startViewerServer inspects the error. For EADDRINUSE on a non-loopback host (an inboundSecret is configured, meaning AGENTMEMORY_VIEWER_HOST is set), it does not retry other ports because non-loopback binds require VIEWER_ALLOWED_HOSTS to include the exact port. It warns with this message and skips the viewer entirely.

Source

Thrown at src/viewer/server.ts:370

    }
  });

  server.on("error", (err: NodeJS.ErrnoException) => {
    if (
      err.code === "EADDRINUSE" &&
      inboundSecret === null &&
      attempt < MAX_VIEWER_PORT_RETRIES
    ) {
      attempt++;
      currentPort = requestedPort + attempt;
      setImmediate(tryListen);
      return;
    }
    if (err.code === "EADDRINUSE") {
      boundViewerPort = null;
      viewerSkipped = true;
      if (inboundSecret !== null) {
        console.warn(
          `[agentmemory] Viewer port ${requestedPort} is in use while bound to ${host}; not retrying because non-loopback viewer binds require VIEWER_ALLOWED_HOSTS to match the exact port. Free the port, choose another viewer port, or unset AGENTMEMORY_VIEWER_HOST to keep the safe loopback bind.`,
        );
      } else {
        console.warn(
          `[agentmemory] Viewer ports ${requestedPort}-${requestedPort + MAX_VIEWER_PORT_RETRIES} all in use, skipping viewer.`,
        );
      }
    } else {
      boundViewerPort = null;
      viewerSkipped = true;
      console.error(`[agentmemory] Viewer error:`, err.message);
    }
  });

  tryListen();

  return server;
}

View on GitHub (pinned to e04ba88819)

Solutions

  1. Free the requested port (find the process with lsof -i :PORT or ss -ltnp and stop it).
  2. Set a different viewer port via the viewer port configuration so it doesn't collide.
  3. Unset AGENTMEMORY_VIEWER_HOST to return to the safe loopback bind, which can retry the next port.
  4. If binding non-loopback intentionally, ensure VIEWER_ALLOWED_HOSTS matches the exact host:port.

Example fix

// before
AGENTMEMORY_VIEWER_HOST=0.0.0.0  # port 49135 already in use, no retry
// after
unset AGENTMEMORY_VIEWER_HOST  # loopback bind retries next port
# or
lsof -ti :49135 | xargs kill
Defensive patterns

Strategy: validation

Validate before calling

import { createServer } from "node:net";
export function isPortFree(port: number, host = "0.0.0.0"): Promise<boolean> {
  return new Promise((resolve) => {
    const s = createServer();
    s.once("error", () => resolve(false));
    s.once("listening", () => s.close(() => resolve(true)));
    s.listen(port, host);
  });
}

Prevention

When it happens

Trigger: startViewerServer (called by viewerServer/viewer/server) gets err.code === 'EADDRINUSE' while host is non-loopback (inboundSecret !== null): the requested viewer port is occupied by another process, and port-hopping is disabled for non-loopback binds.

Common situations: Another agentmemory daemon or dev server already listening on the requested port; stale process holding the port; setting AGENTMEMORY_VIEWER_HOST=0.0.0.0 in docker where the port is taken by a sidecar; two daemons started from the same config.

Related errors


AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30). Data as JSON: /api/errors/4d78970b890df409. Report an issue: GitHub.