musistudio/claude-code-router · error · Error

ToolHub resolver requires TOOLHUB_OPENAI_API_KEY and TOOLHUB

Error message

ToolHub resolver requires TOOLHUB_OPENAI_API_KEY and TOOLHUB_OPENAI_MODEL.

What it means

Thrown when the ToolHub resolver (which uses an OpenAI-compatible chat model to map a natural-language query to catalog tools) has neither an API key nor a model configured. It checks config values first, then the TOOLHUB_OPENAI_API_KEY and TOOLHUB_OPENAI_MODEL environment variables, and throws if either is missing.

Source

Thrown at packages/core/src/mcp/toolhub-mcp.ts:1482

    openAiModel?: string;
  }) {}

  async search(input: {
    catalog: SearchCatalogItem[];
    code?: string;
    query: string;
    timeoutMs?: number;
    topK?: number;
  }): Promise<SearchResult> {
    const query = input.query.trim();
    if (!query) {
      throw new Error("ToolHub resolve query must be non-empty.");
    }
    const apiKey = this.config.openAiApiKey || env("TOOLHUB_OPENAI_API_KEY");
    const baseURL = this.config.openAiBaseUrl || env("TOOLHUB_OPENAI_BASE_URL") || "https://api.openai.com/v1";
    const model = this.config.openAiModel || env("TOOLHUB_OPENAI_MODEL");
    if (!apiKey || !model) {
      throw new Error("ToolHub resolver requires TOOLHUB_OPENAI_API_KEY and TOOLHUB_OPENAI_MODEL.");
    }

    const topK = normalizeTopK(input.topK);
    const timeoutMs = normalizeSearchTimeout(input.timeoutMs);
    const deadlineAt = Date.now() + timeoutMs;
    await waitForLocalResolverEndpoint(baseURL, apiKey, timeoutMs);
    const client = new OpenAI({ apiKey, baseURL });
    const messages: SearchMessage[] = [
      {
        role: "user",
        content: JSON.stringify({
          context: input.code ?? "",
          query
        }, null, 2)
      }
    ];

    let didCallAnalyzer = false;

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Set TOOLHUB_OPENAI_API_KEY and TOOLHUB_OPENAI_MODEL in the environment where the process runs
  2. Or pass openAiApiKey and openAiModel explicitly in the ToolHub config object
  3. For local/CCR gateways, also set TOOLHUB_OPENAI_BASE_URL and ensure the gateway is up

Example fix

// before
const toolhub = new ToolHubClient({});

// after
const toolhub = new ToolHubClient({ openAiApiKey: process.env.TOOLHUB_OPENAI_API_KEY!, openAiModel: process.env.TOOLHUB_OPENAI_MODEL! });
Defensive patterns

Strategy: validation

Validate before calling

const required = ["TOOLHUB_OPENAI_API_KEY", "TOOLHUB_OPENAI_MODEL"];
const missing = required.filter(k => !process.env[k]);
if (missing.length) throw new Error(`Missing env: ${missing.join(", ")}`);

Try / catch

try {
  await toolhub.resolve({ query });
} catch (e) {
  if (e instanceof Error && e.message.includes("TOOLHUB_OPENAI")) {
    // configuration problem: fail fast with clear ops message
    failDeployment(e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: Invoking resolve() without setting openAiApiKey/openAiModel in config and without both TOOLHUB_OPENAI_API_KEY and TOOLHUB_OPENAI_MODEL in the environment.

Common situations: Missing .env entries in local dev; CI/containers where ToolHub env vars aren't exported; only one of the two vars set (key but no model name); custom baseURL (e.g. local gateway) set but key/model forgotten.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


AI-assisted analysis of musistudio/claude-code-router@99f24806c6 (2026-08-27). Data as JSON: /api/errors/3848107ad0f82134. Report an issue: GitHub.