#4092·claude-mem

BUG OpenRouter provider: worker fetch fails while identical Bun fetch succeeds

Author: intellectualpeacefulCreated Sep 15, 2026Updated Sep 16, 2026

Environment

  • claude-mem: 13.24.23
  • Bun: 1.4.2
  • OS: macOS
  • Provider: openrouter (custom OpenAI-compatible endpoint, not openrouter.ai)
  • Base URL: http://192.168.178.166:11434/v1 (remote Ollama instance)
  • Model: llama3.1:8b

Problem

The worker's OpenRouter provider fails on every attempt to reach a self-hosted OpenAI-compatible endpoint (Ollama), immediately and consistently, with:

OpenRouter network error: Was there a typo in the url or port?

This is the t.cause.message surfaced by Bun when fetch() rejects with no HTTP response ever received (see y4's e===void 0 branch in worker-service.cjs). The failure happens in ~150-400ms — far too fast to be the configured CLAUDE_MEM_LLM_TIMEOUT_MS=300000.

It persists identically across a full npx claude-mem restart (new PID, same config, same immediate failure).

What works against the exact same endpoint

Test Result
GET /v1/models via curl 200
POST /v1/chat/completions via curl 200
Standalone Bun script, plain fetch() 200
Bun script run as a detached background process (spawn(..., {detached:true, stdio:'ignore'}), PPID reparented to 1, matching the worker's real spawn pattern when setsid is unavailable) 200
Literal replica of the bundle's own request path — verbatim-copied tbe (URL builder), rbe (headers), g$t (body), nbe (400-retry wrapper), fetchChatCompletion, and aR (outer retry/AbortController wrapper), fed the real values from ~/.claude-mem/settings.json, run standalone outside the worker 200, valid JSON response, AbortSignal aborted=false at call time

Only the actual running worker process fails. The replica above was run concurrently with the live worker, against the same endpoint, at the same time the worker was failing in its logs.

What was excluded as a cause

  • DNS / IPv4 vs IPv6 resolution differences
  • Bun version mismatch (verified same Bun.version in worker and tests)
  • Proxy / env vars (HTTP_PROXY, HTTPS_PROXY, NO_PROXY, NODE_TLS_REJECT_UNAUTHORIZED — none set, and grep confirms none of these are read on the OpenRouter code path)
  • Global fetch/http/https overrides, custom dispatcher/agent (undici, setGlobalDispatcher, ProxyAgent, new Agent( — zero matches in the provider's actual code path)
  • Request payload/headers/size (large realistic 122KB payload with real headers tested standalone, succeeded)
  • Concurrency (6 simultaneous standalone requests, all succeeded)
  • AbortSignal reuse/already-aborted state (confirmed aborted=false at the moment of the real call in the literal replica)
  • Timeout (CLAUDE_MEM_LLM_TIMEOUT_MS=300000; real failures occur in ~150-400ms)
  • Stale worker state (fresh restart, new PID, fails identically)

Relevant code paths (worker-service.cjs, 13.24.23)

  • URL builder: tbe(t)
  • Headers: rbe(t,e)
  • Body: g$t(t)
  • Fetch call site: fetchChatCompletion(...), uses the plain global fetch
  • Retry wrapper: aR(t,e) — creates a fresh AbortController per attempt
  • Error classifier: y4, e===void 0 branch produces the exact observed message

Ask

We're not asking for Ollama support — a custom OpenAI-compatible base URL is already configurable and works from every reproduction outside the worker. What we could not find is a reproducible difference in the code between the working standalone path and the failing in-worker path. We'd appreciate any pointers on what's different about the worker process's actual execution context for fetch() (spawn env, detached-process behavior, something specific to how the worker is actually launched in production vs. our reproduction) that could explain this discrepancy.

Not related to any third-party launcher/wrapper — reproduced with the standalone claude-mem CLI/worker only.