musistudio/claude-code-router · error · Error

Resolve retrieval did not converge on any valid catalog tool

Error message

Resolve retrieval did not converge on any valid catalog tools.

What it means

Thrown when the ToolHub resolve loop produced zero valid catalog tool names and no AST-analyzer round ever ran. This is the pure-LLM failure path: the resolver model's response contained no usable tool selections before any AST refinement was attempted.

Source

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

            summary,
            workflowSketch
          })
        : selectedToolNames.length === 0
          ? "Your current answer resolved to zero valid catalog tools. Call the tree-sitter tool on a revised TypeScript workflow sketch before answering."
          : undefined;
      if (refinementFeedback) {
        messages.push(responseMessage);
        messages.push({ role: "user", content: refinementFeedback });
        continue;
      }
      break;
    }

    const selectedToolNames = uniqueStrings([...latestResolvedFromAnalyzer, ...llmSelectedNames]).slice(0, topK);
    if (selectedToolNames.length === 0) {
      throw new Error(didCallAnalyzer || analyzerCallCount > 0
        ? "Resolve retrieval did not converge on any valid catalog tools after AST refinement."
        : "Resolve retrieval did not converge on any valid catalog tools.");
    }
    if (!didCallAnalyzer || analyzerCallCount === 0) {
      referencedTokens = uniqueStrings([...referencedTokens, ...selectedToolNames]);
    }
    if (didCallAnalyzer && analyzerCallCount === 0) {
      throw new Error("Resolve retrieval LLM did not complete an AST planning round.");
    }
    if (!summary) {
      summary = didCallAnalyzer
        ? "Resolved a planned end-to-end tool bundle with AST-assisted retrieval."
        : "Resolved a planned end-to-end tool bundle from the resolver model response.";
    } else if (summary.toLowerCase().includes("no strong tool bundle match was found")) {
      summary = "Resolved a candidate tool bundle after iterative AST refinement.";
    }
    return {
      plannedSteps: plannedSteps.length > 0 ? plannedSteps : undefined,
      referencedTokens,
      selectedToolNames,

View on GitHub (pinned to 99f24806c6)

Solutions

  1. Make the query more specific and domain-aligned with the catalog
  2. Switch to a resolver model that reliably emits tool calls (set TOOLHUB_OPENAI_MODEL)
  3. Check that the catalog actually contains candidate tools via search
  4. Retry — occasional non-deterministic empty model responses can occur

Example fix

// before
await toolhub.resolve({ query: "stuff" });

// after
await toolhub.resolve({ query: "transcribe an audio file to text with speaker labels", topK: 5 });
Defensive patterns

Strategy: retry

Validate before calling

const candidates = await toolhub.search({ query, topK: 3 });
if (candidates.results.length === 0) return { tools: [] }; // skip LLM resolve

Try / catch

try {
  return await toolhub.resolve({ query });
} catch (e) {
  if (e instanceof Error && e.message.includes("did not converge")) {
    await delay(1000);
    return await toolhub.resolve({ query: clarify(query) }); // one retry with rephrase
  }
  throw e;
}

Prevention

When it happens

Trigger: resolve() ends with empty selection sets and didCallAnalyzer/analyzerCallCount are both falsy — the first LLM completion returned no tool calls or only invalid names.

Common situations: Ambiguous or off-domain query so the model selects nothing; a weak model that answers in prose instead of tool_calls; prompt/model misconfiguration producing empty responses.

Related errors


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