Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/content/docs/user-guide/concepts/agents/hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ Most event properties are read-only to prevent unintended modifications. However
- [`AfterToolCallEvent`](@api/python/strands.hooks.events#AfterToolCallEvent)
- `result` - Modify the tool result. See [Result Modification](#result-modification).
- `retry` - Request a retry of the tool invocation. See [Tool Call Retry](#tool-call-retry).
- `exception` *(read-only)* - The original exception if the tool raised one, otherwise `None`. See [Exception Handling](#exception-handling).

- [`AfterInvocationEvent`](@api/python/strands.hooks.events#AfterInvocationEvent)
- `resume` - Trigger a follow-up agent invocation with new input. See [Invocation resume](#invocation-resume).
Expand Down Expand Up @@ -897,6 +898,49 @@ result = agent("What is the capital of France?")
</Tab>
</Tabs>

### Exception Handling

When a tool raises an exception, the agent converts it to an error result and returns it to the model, allowing the model to adjust its approach and retry. This works well for expected errors like validation failures, but for unexpected errors—assertion failures, configuration errors, or bugs—you may want to fail immediately rather than let the model retry futilely. The `exception` property on `AfterToolCallEvent` provides access to the original exception, enabling hooks to inspect error types and selectively propagate those that shouldn't be retried:

<Tabs>
<Tab label="Python">

```python
Comment thread
zastrowm marked this conversation as resolved.
class PropagateUnexpectedExceptions(HookProvider):
"""Re-raise unexpected exceptions instead of returning them to the model."""

def __init__(self, allowed_exceptions: tuple[type[Exception], ...] = (ValueError,)):
self.allowed_exceptions = allowed_exceptions

def register_hooks(self, registry: HookRegistry) -> None:
registry.add_callback(AfterToolCallEvent, self._check_exception)

def _check_exception(self, event: AfterToolCallEvent) -> None:
if event.exception is None:
return # Tool succeeded
if isinstance(event.exception, self.allowed_exceptions):
return # Let model retry these
raise event.exception # Propagate unexpected errors
```

```python
# Usage
agent = Agent(
model=model,
tools=[my_tool],
hooks=[PropagateUnexpectedExceptions(allowed_exceptions=(ValueError, ValidationError))],
)
```

</Tab>
<Tab label="TypeScript">

```ts
// This feature is not yet available in TypeScript SDK
```
</Tab>
</Tabs>

### Tool Call Retry

Useful for implementing custom retry logic for tool invocations. The `AfterToolCallEvent.retry` field allows hooks to request that a tool be re-executed—for example, to handle transient errors, timeouts, or flaky external services. When `retry` is set to `True`, the tool executor discards the current result and invokes the tool again with the same `tool_use_id`.
Expand Down
Loading