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

> Learn how Needle handles unknown tool errors gracefully. Discover error semantics and degradation techniques to ensure agent workflow continuity. Read the complete guide.

- Repository: [Cactus Compute, Inc./needle](https://github.com/cactus-compute/needle)
- Tags: how-to-guide
- Published: 2026-08-16

---

**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](https://github.com/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](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py). During construction, the `Needle` instance populates an internal `_functions` registry from the `tools` argument:

```python
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](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py#L130-L132):

```python
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:

```python
from needle import Needle

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

```

Output:

```python
[{'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:

```python
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:

```python
[
    {'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:

```python
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.

## Related Source Files

Understanding the complete tool system requires examining these components:

- **[needle/__init__.py](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py)** — Core `Needle` class with the `run` method and error handling logic at lines 130-132
- **[needle/agent/tools.py](https://github.com/cactus-compute/needle/blob/main/needle/agent/tools.py)** — Tool registration utilities including the `@tool` decorator for automatic schema generation
- **[tests/test_tools.py](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/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](https://github.com/cactus-compute/needle/blob/main/needle/__init__.py#L130-L132) 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`](https://github.com/cactus-compute/needle/blob/main/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`](https://github.com/cactus-compute/needle/blob/main/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.