-
Notifications
You must be signed in to change notification settings - Fork 571
fix(openai-agents): Avoid double span exit on exception #5174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
fix(openai-agents): Avoid double span exit on exception #5174
Conversation
…webb/openai-agents-span-management
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## webb/store-span-on-openai-agents-context-wrapper #5174 +/- ##
====================================================================================
- Coverage 83.91% 83.87% -0.05%
====================================================================================
Files 181 181
Lines 18342 18379 +37
Branches 3260 3267 +7
====================================================================================
+ Hits 15392 15415 +23
- Misses 1944 1956 +12
- Partials 1006 1008 +2
|
| # Handled in _run_single_turn() patch. | ||
| raise exc.original from None | ||
| except Exception as exc: | ||
| # Invoke agent span is not finished in this case. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could add a get_current_span() again in this case and we would never lose spans.
You also can't guarantee that the SDK does not crash if you try to exit a span you obtain with get_current_span(), so I decided the just drop the span for now.
Much of AgentRunner.run() that is not spent in _run_single_turn() is before the agent invocation span is created anyway. The only cases in which we lose a span is that any of the post-turn steps like output guardrails raise an exception that is not an AgentsException.
| current_span = sentry_sdk.get_current_span() | ||
| if current_span is not None and current_span.timestamp is None: | ||
| current_span.__exit__(None, None, None) | ||
| context_wrapper = getattr(exc.run_data, "context_wrapper", None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: AttributeError when accessing exc.run_data without checking existence
The code accesses exc.run_data.context_wrapper using getattr but doesn't first verify that exc has a run_data attribute. If AgentsException lacks a run_data attribute, accessing exc.run_data will raise an AttributeError before getattr is called. The code should use getattr(exc, "run_data", None) first to safely check for the attribute's existence, consistent with the defensive pattern used for context_wrapper.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In practice it's always set as AgentRunner.run() has a big try-except as the top-level.
| _record_exception_on_span(span, exc) | ||
| end_invoke_agent_span(context_wrapper, agent) | ||
|
|
||
| raise _SingleTurnException(exc) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I created _SingleTurnException to avoid calling capture_exception() twice.
Otherwise, exceptions raised in _run_single_turn() that are not of type AgentsException would be captured in both run() and _run_single_turn().
| _capture_exception(exc) | ||
| raise exc from None | ||
|
|
||
| end_invoke_agent_span(run_result.context_wrapper, agent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: run_result.context_wrapper is accessed directly without defensive checking, risking an AttributeError if the attribute is missing.
Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
Line 66 in runner.py directly accesses run_result.context_wrapper without checking if the attribute exists. If the RunResult object returned by agents.Runner.run() lacks a context_wrapper attribute, an AttributeError will be raised, causing an application crash. This contrasts with the exception handling path (lines 42-45), which defensively uses getattr(exc.run_data, "context_wrapper", None).
💡 Suggested Fix
Replace direct access run_result.context_wrapper with getattr(run_result, "context_wrapper", None) to safely retrieve the attribute, similar to the exception path.
🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: sentry_sdk/integrations/openai_agents/patches/runner.py#L66
Potential issue: Line 66 in `runner.py` directly accesses `run_result.context_wrapper`
without checking if the attribute exists. If the `RunResult` object returned by
`agents.Runner.run()` lacks a `context_wrapper` attribute, an `AttributeError` will be
raised, causing an application crash. This contrasts with the exception handling path
(lines 42-45), which defensively uses `getattr(exc.run_data, "context_wrapper", None)`.
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 4596996
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate of #5174 (comment)
Description
With #5165 we have a handle on the agent invocation span, as the span is now attached to the context wrapper returned from the
AgentRunner.run()method.Exceptions of type
AgentsExceptionare now caught onAgentRunner.run()instead ofAgentRunner._run_single_turn(), becauseopenai-agentsattaches the context wrapper to these exceptions in the method.Removing
sentry_sdk.get_current_span()avoids double exit scenarios when the active span is not an agent invocation span. This is the case when theasynciointegration is used.Previously, the script below results in a double span exit unhandled exception.
Issues
Reminders
tox -e linters.feat:,fix:,ref:,meta:)