How Needle Handles Unknown Tool Errors: A Complete Guide to Error Semantics and Graceful Degradation

Needle handles unknown tool errors by returning a structured error dictionary with the message "unknown tool: {name}" rather than raising an exception, allowing the agent workflow to continue executing remaining tool calls.

The Needle framework, an open-source agent orchestration library from cactus-compute/needle, provides explicit error semantics for tool resolution failures. When the underlying language model attempts to invoke a tool that has not been registered with the agent, Needle captures this condition gracefully and surfaces it in a predictable, machine-readable format.

Where Needle Resolves Tool Names

Tool resolution occurs in the core execution loop of the Needle class, located in needle/init.py. During construction, the Needle instance populates an internal _functions registry from the tools argument:

from needle import Needle, tool

@tool
def search(query: str) -> str:
    """Search the knowledge base."""
    return f"Results for: {query}"

agent = Needle(tools=[search])

# Internal: self._functions = {"search": <function search>}

The _functions dictionary maps string tool names to their corresponding Python callables. This registry is the single source of truth for what tools Needle can execute.

The Unknown Tool Error Handling Mechanism

When Needle.run() processes a query, it parses the model's response into a list of function calls. For each call, Needle performs a dictionary lookup at needle/init.py line 130-132:

fn = self._functions.get(call.get("name"))
if fn is None:
    results.append({"error": "unknown tool: " + str(call.get("name"))})
    continue

This implementation demonstrates three key design decisions:

  • Graceful degradation: The continue statement skips to the next tool call instead of terminating execution.
  • Structured errors: Errors are dictionaries with an "error" key, matching the format of other failure modes.
  • Clear attribution: The unrecognized tool name is preserved in the error message for debugging.

The complete error flow ensures that partial failures do not invalidate successful tool executions elsewhere in the same query.

Practical Code Examples

Basic Unknown Tool Scenario

This example shows the minimal case where no tools are registered and the model attempts a function call anyway:

from needle import Needle

agent = Needle(tools=[])
response = agent.run("What is the weather today?")
print(response["results"])

Output:

[{'error': 'unknown tool: get_weather'}]

Needle returns the error in the results array without raising an exception, allowing the calling code to inspect and handle the condition.

Mixed Success and Failure

When some tools exist and others do not, Needle processes each call independently:

from needle import Needle, tool

@tool
def calculate(expression: str) -> float:
    """Evaluate a mathematical expression."""
    return eval(expression)

agent = Needle(tools=[calculate])
response = agent.run("Calculate 2+2 and also fetch the news")

Result structure:

[
    {'result': 4.0},
    {'error': 'unknown tool: fetch_news'}
]

The successful calculate execution appears alongside the unknown tool failure, demonstrating Needle's per-call error isolation.

Error Handling in Production Code

Production implementations should inspect results for error conditions:

from needle import Needle, tool

@tool
def get_user(user_id: int) -> dict:
    """Retrieve user information."""
    return {"id": user_id, "name": "Example"}

agent = Needle(tools=[get_user])
response = agent.run("Get user 123 and delete user 456")

for idx, result in enumerate(response["results"]):
    if "error" in result:
        print(f"Call {idx} failed: {result['error']}")
        if "unknown tool" in result["error"]:
            # Trigger fallback or notify operator

            pass
    else:
        print(f"Call {idx} succeeded: {result}")

This pattern enables robust error recovery without try/except blocks for tool resolution failures.

Comparison with Alternative Error Strategies

Approach Implementation Trade-off
Needle's graceful error return Error dict in results, execution continues Maximum robustness, requires caller inspection
Exception raising raise ToolNotFoundError(name) Immediate failure, simpler for strict requirements
Silent skipping continue without error recording Cleaner output, loses debugging information
Mock fallback Return placeholder result Maintains flow, potentially misleading

Needle's approach prioritizes observability and workflow completion over fast-failure semantics, which aligns with agent use cases where partial results remain valuable.

Understanding the complete tool system requires examining these components:

  • needle/init.py — Core Needle class with the run method and error handling logic at lines 130-132
  • needle/agent/tools.py — Tool registration utilities including the @tool decorator for automatic schema generation
  • tests/test_tools.py — Test coverage for tool resolution including unknown tool scenarios

The error string format "unknown tool: {name}" is a stable contract that tests in test_tools.py verify across versions.

Summary

  • Needle resolves tool names against an internal _functions registry populated from the tools constructor argument
  • Unknown tool errors are captured at needle/init.py lines 130-132 using dict.get() with None-checking
  • Errors are returned as {"error": "unknown tool: {name}"} dictionaries in the results array, not raised as exceptions
  • Execution continues for remaining tool calls, enabling partial success scenarios
  • All tool execution results share the same dictionary structure, simplifying downstream error handling

Frequently Asked Questions

What happens if Needle receives multiple unknown tool calls in one query?

Needle processes each function call independently. Every unrecognized tool name generates a separate error dictionary in the results array, while recognized tools execute normally. The response contains mixed success and error entries in call order.

Can I customize the unknown tool error message or behavior?

The current implementation in needle/__init__.py uses a hardcoded error format. To modify behavior, you would need to subclass Needle and override the run method's tool resolution loop. The framework does not expose hooks for pluggable error handlers as of the latest main branch.

How does Needle distinguish between unknown tools and tools that raise exceptions during execution?

Both conditions produce {"error": ...} dictionaries, but the content differs. Unknown tools return "unknown tool: {name}" at resolution time. Runtime exceptions in known tools are caught internally and typically include traceback information or the exception message in the error value. Inspect the error string prefix to differentiate resolution failures from execution failures.

Is the unknown tool error format stable across Needle versions?

The "unknown tool: {name}" format is a documented behavioral contract. The test suite in tests/test_tools.py asserts this exact string structure, suggesting stability. However, as with any pre-1.0 open-source project, pinning to specific commits or monitoring release notes is recommended for production dependencies.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →