thedotmack/claude-mem · error · HostObserverUnavailableError

CLAUDE_MEM_HOST_OBSERVER_PORT=${configured} is occupied by a

Error message

CLAUDE_MEM_HOST_OBSERVER_PORT=${configured} is occupied by a process that is not an OpenAI-compatible observer. Stop that process or point CLAUDE_MEM_HOST_OBSERVER_PORT at your observer.

What it means

When CLAUDE_MEM_HOST_OBSERVER_PORT is set, the port must host an OpenAI-compatible endpoint (the probe hits /v1/models and /v1/chat/completions and checks the response shape). If something answers but is not an observer — or the port refuses a bind test during the sync probe — claude-mem refuses to use it rather than misidentifying the service.

Source

Thrown at src/npx-cli/cmem-memory-credentials.ts:249

): string {
  const worker = parsePort(typeof workerPort === 'number' ? workerPort : nonEmptyString(workerPort));
  const configuredRaw = nonEmptyString(env.CLAUDE_MEM_HOST_OBSERVER_PORT);
  if (configuredRaw) {
    const configured = parsePort(configuredRaw);
    if (configured == null) {
      throw new HostObserverUnavailableError(
        `CLAUDE_MEM_HOST_OBSERVER_PORT=${configuredRaw} is not a valid port. Set it to the port your OpenAI-compatible observer already listens on.`,
      );
    }
    if (worker != null && configured === worker) {
      throw new HostObserverUnavailableError(
        `CLAUDE_MEM_HOST_OBSERVER_PORT=${configured} is the claude-mem worker port. Point it at your OpenAI-compatible observer instead.`,
      );
    }
    const status = probe(configured);
    if (status === 'observer') return String(configured);
    if (status === 'occupied') {
      throw new HostObserverUnavailableError(
        `CLAUDE_MEM_HOST_OBSERVER_PORT=${configured} is occupied by a process that is not an OpenAI-compatible observer. Stop that process or point CLAUDE_MEM_HOST_OBSERVER_PORT at your observer.`,
      );
    }
    throw new HostObserverUnavailableError(
      `CLAUDE_MEM_HOST_OBSERVER_PORT=${configured} has nothing listening. Start your OpenAI-compatible observer on that port, then rerun with --provider host.`,
    );
  }

  const candidates = hostObserverCandidatePorts(workerPort, env);
  for (const port of candidates) {
    const status = probe(port);
    if (status === 'observer') return String(port);
  }

  throw new HostObserverUnavailableError(
    `No OpenAI-compatible host observer is listening on 127.0.0.1:${candidates.join(' or ') || HOST_OBSERVER_DEFAULT_PORT}. Host mode uses an observer you already run; claude-mem does not start one. Start your observer, then rerun with --provider host. If it uses another port, set CLAUDE_MEM_HOST_OBSERVER_PORT.`,
  );
}

View on GitHub (pinned to 8bc631a71a)

Solutions

  1. Stop the process occupying that port (lsof -i :PORT, then kill it), and rerun the install
  2. Find your observer's real API port and set CLAUDE_MEM_HOST_OBSERVER_PORT to it, e.g. export CLAUDE_MEM_HOST_OBSERVER_PORT=4141
  3. Verify the endpoint responds like OpenAI: curl -H 'Authorization: Bearer x' http://127.0.0.1:$CLAUDE_MEM_HOST_OBSERVER_PORT/v1/models should return a model list
  4. Unset CLAUDE_MEM_HOST_OBSERVER_PORT to let auto-discovery probe 37777/37778 instead

Example fix

// before
export CLAUDE_MEM_HOST_OBSERVER_PORT=3000  # occupied by a dev server
// after
lsof -ti :3000 | xargs kill
export CLAUDE_MEM_HOST_OBSERVER_PORT=4141  # actual observer port
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(`http://127.0.0.1:${port}/v1/models`, { headers: { Authorization: 'Bearer x' } }).catch(() => null);
const body = res?.ok ? await res.text() : '';
if (!body.includes('data')) throw new Error(`Port ${port} is not an OpenAI-compatible observer`);

Try / catch

try {
  const port = resolveHostObserverPort(workerPort, env);
} catch (e) {
  if (e instanceof HostObserverUnavailableError && e.message.includes('is occupied by a process')) {
    // inspect/kill the squatter or reconfigure the port, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: resolveHostObserverPort probes the configured port; probeHostObserverPortSync returns 'occupied' because another process (a dev server, another app, a stale worker) is listening there and its responses do not look OpenAI-compatible.

Common situations: Port reused by a unrelated local web app; leftover process from a previous session squatting on the port; pointing at a UI port of a tool whose API is on a different port.

Related errors


AI-assisted analysis of thedotmack/claude-mem@8bc631a71a (2026-09-09). Data as JSON: /api/errors/0ea0159e089e40bc. Report an issue: GitHub.