JuliusBrussee/caveman · error

no named tools found

Error message

no named tools found

What it means

extractTools iterates the recognized list and keeps only entries that yield a non-empty tool name (via "name" or nested "function.name"). If none survive — the list is empty or every entry lacks a name — it reports 'no named tools found' rather than returning an empty catalog.

Solutions

  1. Verify each tool entry has a "name" (or "function.name" for OpenAI shape) and fix typos
  2. Check the upstream server actually exposes tools — connect and list tools before exporting
  3. Lint a non-empty catalog: `{"tools":[{"name":"search","inputSchema":{...}}]}`
  4. Inspect the input with `jq '.tools | length'` to confirm tools exist

Example fix

// before
{"tools": [{"description": "search the web"}]}
// after
{"tools": [{"name": "web_search", "description": "search the web", "inputSchema": {"type": "object", "properties": {}}}]}}
Defensive patterns

Strategy: validation

Validate before calling

const tools = JSON.parse(input).tools ?? []
const named = tools.filter(t => typeof t?.name === 'string' && t.name.length > 0)
if (named.length === 0) throw new Error('catalog has no named tools')

Type guard

function hasNamedTools(catalog: unknown): boolean {
  const list = Array.isArray(catalog) ? catalog
    : (catalog as any)?.tools ?? (catalog as any)?.functions ?? []
  return list.some((t: any) => typeof t?.name === 'string' && t.name) ||
         list.some((t: any) => typeof t?.function?.name === 'string' && t.function.name)
}

Try / catch

tools, err := extractTools(input)
if err != nil && err.Error() == "no named tools found" {
  return fmt.Errorf("catalog empty or tools missing 'name' field; check upstream server")
}

Prevention

When it happens

Trigger: Passing `{"tools":[]}`, an array of objects without "name" fields, or OpenAI entries missing the nested "function" object; also a single tool object whose name field is empty or misspelled (e.g. "tool_name").

Common situations: Exporting tools from a server that returned zero tools (all filtered or disabled); hand-written catalogs with a typo in the name key; responses truncated so tool objects lost their fields.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/1cfb9d8fc647576e. Report an issue: GitHub.

Appendix: source

Thrown at shrink/shrink.go:281

		obj, ok := item.(map[string]any)
		if !ok {
			continue
		}
		// OpenAI nests under "function".
		toolObj := obj
		if fn, ok := obj["function"].(map[string]any); ok {
			toolObj = fn
		}
		name, _ := toolObj["name"].(string)
		if name == "" {
			continue
		}
		raw, _ := json.Marshal(obj)
		schema := schemaOf(toolObj)
		entries = append(entries, toolEntry{name: name, raw: raw, schema: schema})
	}
	if len(entries) == 0 {
		return nil, fmt.Errorf("no named tools found")
	}
	return entries, nil
}

// schemaOf returns a tool's parameter schema, under either "inputSchema" (MCP) or
// "parameters" (OpenAI).
func schemaOf(tool map[string]any) map[string]any {
	if s, ok := tool["inputSchema"].(map[string]any); ok {
		return s
	}
	if s, ok := tool["parameters"].(map[string]any); ok {
		return s
	}
	return nil
}

// profileOf extracts param names, per-param enums, and the required list.
func profileOf(schema map[string]any) ToolProfile {

View on GitHub (pinned to 3ee70a1026)