vxcontrol/pentagi · error

unknown tool: %s

Error message

unknown tool: %s

What it means

The memory tool dispatcher received a tool `name` that is not one of the tool names handled by its `switch` (e.g. not `SearchInMemoryToolName` or the other memory tool names). The library returns a generic 'unknown tool' error so callers know the name is not registered on this tool implementation.

Source

Thrown at backend/pkg/tools/memory.go:243

			queriesText := strings.Join(action.Questions, "\n--------------------------------\n")
			_, _ = m.vslp.PutLog(
				ctx,
				agentCtx.ParentAgentType,
				agentCtx.CurrentAgentType,
				filtersData,
				queriesText,
				database.VecstoreActionTypeRetrieve,
				buffer.String(),
				action.TaskID.PtrInt64(),
				action.SubtaskID.PtrInt64(),
			)
		}

		return buffer.String(), nil

	default:
		logger.Error("unknown tool")
		return "", fmt.Errorf("unknown tool: %s", name)
	}
}

func (m *memory) IsAvailable() bool {
	return m.store != nil
}

func getGlobalFilters(filters map[string]any) (bool, map[string]any) {
	globalFilters := maps.Clone(filters)
	delete(globalFilters, "task_id")
	delete(globalFilters, "subtask_id")
	return len(globalFilters) != len(filters), globalFilters
}

View on GitHub (pinned to ea665308ba)

Solutions

  1. Log the received `name` and compare it against the exported `*ToolName` constants in the package.
  2. Ensure only the intended tool definitions are advertised to the LLM so it cannot select unregistered names.
  3. Verify the dispatcher routes each tool name to the correct implementation (memory vs search tool).
  4. Check for casing/renaming drift between the model's tool list and the switch cases.

Example fix

// before
out, err := mem.Handle(ctx, "search_in_memory ", args) // trailing space/casing mismatch
// after
out, err := mem.Handle(ctx, tools.SearchInMemoryToolName, args) // use exported constants
Defensive patterns

Strategy: type-guard

Validate before calling

knownTools := map[string]bool{
    tools.SearchInMemoryToolName: true,
    tools.StoreInMemoryToolName:  true,
}
if !knownTools[name] {
    return fmt.Errorf("tool %q not registered for memory handler", name)
}

Type guard

func isMemoryTool(name string) bool {
    switch name {
    case tools.SearchInMemoryToolName, tools.StoreInMemoryToolName:
        return true
    }
    return false
}

Try / catch

out, err := handler.Handle(ctx, name, args)
if err != nil && strings.HasPrefix(err.Error(), "unknown tool:") {
    logger.Warnf("routing miss for tool %q, falling back to registry lookup", name)
    return registry.Dispatch(ctx, name, args)
}

Prevention

When it happens

Trigger: Calling `memory.Handle(ctx, name, args)` with a name string that doesn't match any `case` in the switch — typically a typo, wrong casing, or passing a tool name belonging to another tool implementation (e.g. `search_answer`).

Common situations: Agent framework routes tool calls by model-emitted name and the model hallucinated a tool name; tool registry wiring registered the memory tool under a different constant; refactoring renamed a tool constant but old prompts/caches still use the previous name.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/cde65c6584659afb. Report an issue: GitHub.