Troubleshooting
Nothing appears in the viewer
Section titled “Nothing appears in the viewer”Work down this list; it is ordered by how often each one is the answer.
-
Is the server actually up?
Terminal window curl -s http://127.0.0.1:4747/health# {"ok":true,"name":"graphmind-ai","version":"0.3.2"}No response means
graphmindis not running, or is on another port. -
Do the ports match? The app dials
ws://127.0.0.1:4747/ingestby default. If you started the server with--port 4848, tell the app:Terminal window GRAPHMIND_URL=ws://127.0.0.1:4848/ingest node agent.js -
Is GraphMind disabled? Print what the session thinks:
console.log(gm.session.stats());// { enabled, attached, buffered, dropped, heldGates, seq }enabled: falsemeans a kill switch fired —GRAPHMIND_DISABLED=1, orNODE_ENV=productionwithoutGRAPHMIND=1. See environment. -
Did the process exit before the handshake? The transport is lazy and the handshake takes up to ~1.3 s. A script that finishes immediately can exit first. Add
await gm.ready()before the work andawait gm.dispose()after it.If your process dies silently at
await gm.ready()with no debugger listening — no output, a non-zero exit, the run body never entered — you are on a version before 0.3.2, whereready()’s own timer could not hold the event loop open. Upgrade; no workaround is needed any more. -
Are you actually emitting?
enabled: true, attached: false, seq: 0means the session connected to nothing and nothing was emitted — usually an un-wrapped model or un-wrapped tools. Check you are using the valueswrapModel/wrapClient/wrapToolsreturned, not the originals. -
Look for a warning. The client warns once per failure kind per minute with a
[graphmind]prefix. It never throws, so the warning is the only signal.
Port 4747 is already in use
Section titled “Port 4747 is already in use”Another graphmind is probably still running.
lsof -nP -iTCP:4747 -sTCP:LISTEN # macOS / Linuxnetstat -ano | findstr :4747 # WindowsKill it, or move both sides:
graphmind --port 4848GRAPHMIND_URL=ws://127.0.0.1:4848/ingest node agent.jsIf you use MCP, pass --port 4848 there too or its deep links will point at the wrong viewer.
Gates never pause
Section titled “Gates never pause”-
Is a viewer attached? Gates fail open by design: detached means no holds, ever. Check
gm.session.stats().attached, and the connection indicator in the run bar. -
Did the run start before the handshake landed? This is the classic one. The transport is lazy, so an agent that starts in the same tick as
graphmind()can run past its first gates before attaching. Fix:await gm.ready(); // explicitconst gm = graphmind({ app: 'x', waitForAttach: true }); // or automatic -
Does anything match? In run mode, only
{ point: 'error' }is armed by default. To break before a node you need a breakpoint on it, or step mode. -
Is the matcher right?
namematches the node’s name, not your variable name. Check the exact label in the viewer — for a tool it is the tool’s registered name.
The agent is stuck / not progressing
Section titled “The agent is stuck / not progressing”Almost certainly held at a gate you did not notice. Look at the run bar for a pause banner and resume it, or clear the breakpoint chips.
If you closed the viewer, gates should already have auto-continued — that is the fail-open guarantee. If your agent is still stuck after the viewer is gone, GraphMind is not what is holding it; look at your own retry loop or your provider.
Guard the case in advance with pauseTimeoutMs:
const gm = graphmind({ app: 'nightly-batch', pauseTimeoutMs: 5 * 60_000 });Timeout errors while debugging
Section titled “Timeout errors while debugging”A held gate can burn an SDK timeout budget. Adapters neutralise this while attached, but the
Vercel AI SDK’s outer loop watches its own merged signal, so totalMs / stepMs / chunkMs can
still abort the surrounding run after a long hold.
Fix: remove timeout configs while debugging.
Details.
“Version mismatch” or events being rejected
Section titled ““Version mismatch” or events being rejected”The envelope’s gm field is the protocol major version, and peers reject a mismatch rather than
guessing. It means your adapter and CLI are from incompatible protocol generations. Update both:
npm i -D @graphmind-ai/sdk@latestnpx graphmind-ai@latest403 Forbidden, or a WebSocket that closes immediately
Section titled “403 Forbidden, or a WebSocket that closes immediately”Since 0.3.0 the server checks the Origin of every request and upgrade, and requires a loopback
Host. Clients that send no Origin at all — the SDK, curl, the CLI — are unaffected; a
browser page from another origin gets a 403.
You will hit this in one ordinary situation: running the viewer from its own dev server
(apps/viewer on Vite port 5199). Allow it explicitly:
GRAPHMIND_ALLOWED_ORIGINS=http://localhost:5199,http://127.0.0.1:5199 graphmindYou will also hit it if you reach the server through a hostname that is not a loopback name —
a .local alias, a container hostname, a rebinding domain. Use 127.0.0.1 or localhost, or
tunnel (an SSH tunnel presents as loopback and works).
GRAPHMIND_ALLOWED_ORIGINS='*' turns the check off. That is a real hole — a page you visit can
then read every recorded run and resume a paused one with an injected result — so only do it on
a machine and network you fully control.
The viewer loads but says the bundle is missing
Section titled “The viewer loads but says the bundle is missing”You are running from a source checkout without a built viewer. The published package bundles it;
in a monorepo run pnpm build:viewer first, or point GRAPHMIND_VIEWER_DIST at a build. The API
and sockets work either way.
Node version errors
Section titled “Node version errors”GraphMind needs Node ≥ 22.13 — it uses the built-in node:sqlite module.
node -vOn an older Node you will see a module-not-found for node:sqlite. Upgrade; there is no
polyfill and adding a native SQLite dependency is a trade the project deliberately does not make.
Concurrent runs are merged into one
Section titled “Concurrent runs are merged into one”Model steps are grouped into invocations by the run context. Outside gm.run all steps share one
scope, so two concurrent calls with the same first message can merge. Wrap each concurrent unit
in its own gm.run.
An imported trace looks sparse
Section titled “An imported trace looks sparse”graphmind import is best-effort across dialects. Spans it does not recognise become generic
nodes or are skipped with a note — the command prints the count. Recognised dialects: Vercel AI
SDK OTel spans, OTel GenAI semantic conventions, OpenInference.
An imported run is history-only by definition: nothing is running, so there is nothing to pause.