{"record":{"id":"d57d4dbf1f78d11f","repo":"musistudio/claude-code-router","slug":"toolhub-resolver-could-not-connect-to-ccr-gateway","errorCode":null,"errorMessage":"ToolHub resolver could not connect to CCR Gateway at ${readinessUrl}: ${formatError(lastError)}.","messagePattern":"ToolHub resolver could not connect to CCR Gateway at (.+?): (.+?)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/core/src/mcp/toolhub-mcp.ts","lineNumber":1739,"sourceCode":"    const timer = setTimeout(() => controller.abort(), 1000);\n    try {\n      await fetch(readinessUrl, {\n        headers: { authorization: `Bearer ${apiKey}` },\n        method: \"GET\",\n        signal: controller.signal\n      });\n      return;\n    } catch (error) {\n      lastError = error;\n      if (!isRetryableLocalResolverError(error)) {\n        return;\n      }\n      await delay(300);\n    } finally {\n      clearTimeout(timer);\n    }\n  }\n  throw new Error(`ToolHub resolver could not connect to CCR Gateway at ${readinessUrl}: ${formatError(lastError)}.`);\n}\n\nfunction localResolverReadinessUrl(baseURL: string): string | undefined {\n  let parsed: URL;\n  try {\n    parsed = new URL(baseURL);\n  } catch {\n    return undefined;\n  }\n  if (!isLoopbackHostname(parsed.hostname)) {\n    return undefined;\n  }\n  const base = baseURL.replace(/\\/+$/g, \"\");\n  return `${base}/models`;\n}\n\nfunction isLoopbackHostname(hostname: string): boolean {\n  const normalized = hostname.trim().toLowerCase().replace(/^\\[|\\]$/g, \"\");","sourceCodeStart":1721,"sourceCodeEnd":1757,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/core/src/mcp/toolhub-mcp.ts#L1721-L1757","documentation":"Thrown after the resolver exhausts its polling budget waiting for a local CCR Gateway (an OpenAI-compatible local proxy used by ToolHub resolve) to become ready at its readiness URL. The last observed connection error is included via formatError. This is a startup/dependency failure, not a query failure.","triggerScenarios":"waitForLocalResolverEndpoint retries against the readiness endpoint derived from TOOLHUB_OPENAI_BASE_URL until timeout, and every attempt fails (connection refused, DNS failure, TLS error), so the throw fires with the last error embedded.","commonSituations":"CCR Gateway not started or still booting when resolve() is called; wrong port in TOOLHUB_OPENAI_BASE_URL; gateway bound to a different interface; firewall blocking localhost connections; readiness URL path not exposed by an older gateway version.","solutions":["Start the CCR Gateway and confirm it listens at the TOOLHUB_OPENAI_BASE_URL port","curl the readiness URL printed in the message to verify it returns 200","Increase the resolve timeoutMs so slow gateway startups can converge","Point TOOLHUB_OPENAI_BASE_URL directly at OpenAI (or another ready endpoint) if you don't need the local gateway"],"exampleFix":"# before\nTOOLHUB_OPENAI_BASE_URL=http://localhost:9999/v1  # gateway not running\n\n# after\nccr-gateway start &  # or correct port\ncurl http://localhost:9999/v1/health\nTOOLHUB_OPENAI_BASE_URL=http://localhost:9999/v1","handlingStrategy":"retry","validationCode":"async function waitGateway(url: string, timeoutMs = 30000) {\n  const deadline = Date.now() + timeoutMs;\n  while (Date.now() < deadline) {\n    try { if ((await fetch(url)).ok) return; } catch {}\n    await new Promise(r => setTimeout(r, 300));\n  }\n  throw new Error(`Gateway not ready: ${url}`);\n}\nawait waitGateway(readinessUrl);","typeGuard":null,"tryCatchPattern":"try {\n  await toolhub.resolve({ query });\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"could not connect to CCR Gateway\")) {\n    await startGateway(); await waitGateway(readinessUrl);\n    return await toolhub.resolve({ query });\n  }\n  throw e;\n}","preventionTips":["Start and health-check the gateway as a supervised dependency before the app accepts traffic","Bump resolve timeoutMs for slow gateway cold starts"],"tags":["gateway","connection","startup","toolhub","localhost"],"backgroundTag":"connection-refused","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}