Skip to content

Conversation

@charles-dyfis-net
Copy link
Contributor

Description

Motivation

Hook providers need the ability to distinguish between expected errors (like validation failures that the model should adjust for and retry) and unexpected errors (like assertion failures or configuration errors that should fail the task immediately). Currently, AfterToolCallEvent.exception is always None for @tool-decorated functions because the decorator catches all exceptions and converts them to error results before the executor can see them. This forces all tool errors to be returned to the model, even when propagating the exception would be more appropriate.

Resolves: #1565

Public API Changes

ToolResultEvent now accepts an optional exception parameter, and AfterToolCallEvent.exception is populated for decorated tools:

# Before: exception is always None for decorated tools
class MyHook(HookProvider):
    def register_hooks(self, registry, **kwargs):
        registry.add_callback(AfterToolCallEvent, self._check)

    def _check(self, event: AfterToolCallEvent):
        # event.exception is always None for @tool functions
        # even when the tool raised an exception
        pass

# After: exception is available for inspection
class PropagateUnexpectedExceptions(HookProvider):
    def __init__(self, allowed_exceptions=(ValueError,)):
        self.allowed_exceptions = allowed_exceptions

    def register_hooks(self, registry, **kwargs):
        registry.add_callback(AfterToolCallEvent, self._check)

    def _check(self, event: AfterToolCallEvent):
        if event.exception is None:
            return
        if isinstance(event.exception, self.allowed_exceptions):
            return  # Let model retry
        raise event.exception  # Propagate unexpected errors

The error result is still returned to the model by default; hooks must explicitly re-raise if they want to propagate.

Use Cases

  • Fail-fast on unexpected errors: Propagate AssertionError, ConfigurationError, etc. instead of letting the model retry futilely
  • Error classification: Log or handle different exception types differently based on their class
  • Selective retry: Allow ValueError to return to the model while propagating other exceptions

Related Issues

#1565

Documentation PR

strands-agents/docs#482

Type of Change

New feature

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Copy link
Member

@zastrowm zastrowm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good implementation! Left some comments/requested-changes of the tests

@zastrowm
Copy link
Member

Looks like there are also some linting failures; uv run hatch run prepare is my goto for fixing/checking

@github-actions github-actions bot added size/m and removed size/m labels Jan 28, 2026
@dbschmigelski
Copy link
Member

Looks like there are also some linting failures; uv run hatch run prepare is my goto for fixing/checking

Yeah thats my bad, I optimistically tried to resolve conflicts in the UI. Should be good now

@codecov
Copy link

codecov bot commented Jan 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@charles-dyfis-net
Copy link
Contributor Author

charles-dyfis-net commented Jan 28, 2026

About the unit test assertion changes suggested --

>>> ValueError("foo") == ValueError("foo")
False

...though when it's the exact same object being returned we can use is.

zastrowm
zastrowm previously approved these changes Jan 28, 2026
dbschmigelski
dbschmigelski previously approved these changes Jan 29, 2026
@dbschmigelski dbschmigelski dismissed stale reviews from zastrowm and themself via 60dad8a February 3, 2026 19:50
@github-actions github-actions bot added size/xl and removed size/m labels Feb 3, 2026
@github-actions github-actions bot added size/m and removed size/xl labels Feb 3, 2026

def __init__(self, tool_result: ToolResult) -> None:
"""Initialize with the completed tool result.
Stores the full Exception object as an instance attribute for debugging while
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we do not want implementation details in our docstrings

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Allow decorators to selectively present exceptions from being returned to the model

7 participants