Skip to content

fix(tracing): skip NoOpTrace context reset on GeneratorExit#3225

Open
adityasingh2400 wants to merge 3 commits into
openai:mainfrom
adityasingh2400:fix/tracing-noop-generator-exit
Open

fix(tracing): skip NoOpTrace context reset on GeneratorExit#3225
adityasingh2400 wants to merge 3 commits into
openai:mainfrom
adityasingh2400:fix/tracing-noop-generator-exit

Conversation

@adityasingh2400

Copy link
Copy Markdown
Contributor

Summary

NoOpTrace.__exit__ unconditionally calls finish(reset_current=True), unlike every other lifecycle type in this module:

  • TraceImpl.__exit__ (line 524): self.finish(reset_current=exc_type is not GeneratorExit)
  • ReattachedTrace.__exit__ (line 342): self.finish(reset_current=exc_type is not GeneratorExit)
  • SpanImpl.__exit__ (line 343): if exc_type is GeneratorExit: ... reset_current = False
  • NoOpSpan.__exit__ (line 234): if exc_type is GeneratorExit: ... reset_current = False

When an async generator wrapped in a NoOpTrace (i.e. tracing is disabled) is finalized by GC from a task other than the one that opened it, resetting the contextvars.Token from a different Context raises ValueError: <Token> was created in a different Context. The other lifecycle types already account for this; NoOpTrace should match.

The fix is a single-line change to follow the same pattern. Behavior under tracing-enabled paths is unchanged; behavior under tracing-disabled paths now matches tracing-enabled paths under GeneratorExit.

Test plan

  • New focused test asserts NoOpTrace.__exit__(GeneratorExit, ...) skips the reset and preserves _prev_context_token.
  • All existing tests/tracing/ (32) and trace processor / agent tracing tests (68) pass.
  • Diff is 22 lines (1 src line + test).

@seratch

seratch commented May 8, 2026

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0693c700a2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/agents/tracing/traces.py Outdated

def __exit__(self, exc_type, exc_val, exc_tb):
self.finish(reset_current=True)
self.finish(reset_current=exc_type is not GeneratorExit)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid leaking NoOpTrace on same-context generator close

When a generator using with trace(..., disabled=True) is closed normally in the same task, __exit__ receives GeneratorExit. This now skips Scope.reset_current_trace, leaving get_current_trace() as NoOpTrace after close. Since create_trace_for_run treats any current trace as active and spans under NoOpTrace become NoOpSpan, later tracing in that context can be silently suppressed.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in efa4c5ab: Try the context reset and swallow the cross-context ValueError so the trace is cleared on same-context close without inheriting NoOpTrace on later spans. Test covers the same-task generator close.

@seratch
seratch marked this pull request as draft May 8, 2026 17:00
@seratch seratch removed the bug Something isn't working label May 12, 2026
NoOpTrace.__exit__ unconditionally called finish(reset_current=True),
unlike TraceImpl, ReattachedTrace, SpanImpl, and NoOpSpan which all
skip the contextvars reset when exiting via GeneratorExit. When an
async generator wrapped in a NoOpTrace is finalized by GC from a
different task than the one that opened it, resetting a Token created
in another Context raises ValueError. This aligns NoOpTrace with the
other lifecycle types so disabled tracing behaves consistently with
enabled tracing under generator-driven cleanup.
…alueError

Skipping reset unconditionally on GeneratorExit handled the cross-task GC
case but left NoOpTrace as the current trace after a normal same-context
generator close, suppressing later tracing in that context. Always attempt
the reset and swallow ValueError for the cross-Context case so both paths
work correctly.
@adityasingh2400
adityasingh2400 force-pushed the fix/tracing-noop-generator-exit branch from c00dc93 to efa4c5a Compare May 19, 2026 23:13
@seratch
seratch marked this pull request as ready for review June 22, 2026 01:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 84e7e07717

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# the trace was entered in another task and the generator is
# closing from the parent's context). Skipping reset here is
# safe: that other Context owns its own contextvar copy.
pass

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Restore the copied context after cross-context close

When an async generator enters a disabled trace in one task and a child task created from that context calls aclose(), reset_current_trace() raises ValueError because the token belongs to the opener's Context, but the closer task's copied Context can still have this same NoOpTrace as the current trace. This new pass clears only the saved token, so the closer task continues with a no-op current trace; subsequent Runner.run/create_trace_for_run in that task sees an active trace and skips creating a real one. Please restore or clear the current Context in this branch when it still points at this trace, instead of only swallowing the error.

Useful? React with 👍 / 👎.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants