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
- Free the requested port (find the process with lsof -i :PORT or ss -ltnp and stop it).
- Set a different viewer port via the viewer port configuration so it doesn't collide.
- Unset AGENTMEMORY_VIEWER_HOST to return to the safe loopback bind, which can retry the next port.
- 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
- Check the viewer port is free before starting a non-loopback-bound daemon.
- Only set AGENTMEMORY_VIEWER_HOST when you also configure VIEWER_ALLOWED_HOSTS correctly.
- Run a single agentmemory daemon per host, or assign distinct viewer ports.
- Prefer the default loopback bind unless remote access is required.
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
- [viewer] API ${fetchOpts.method || 'GET'} ${path} returned $
- POST ${url} failed: ${res.status} ${res.statusText}${suffix}
- ${init?.method || "GET"} ${path} -> ${res.status} ${res.stat
- Cohere embedding failed (${response.status}): ${err}
- Gemini embedding failed (${response.status}): ${err}
AI-assisted analysis of rohitg00/agentmemory@e04ba88819 (2026-08-30).
Data as JSON: /api/errors/4d78970b890df409.
Report an issue: GitHub.