traceloop / traceloop/openllmetry
๐ Bug Report: LangGraph instrumentation incorrectly marks successful astream() executions as ERROR due to GeneratorExit
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 7.4k
- Forks
- 1.1k
- Avg merge
- 8d 14h
- Merged PRs (30d)
- 2
Description
Which component is this bug for?
Langchain Instrumentation
๐ Description
While using the TraceLoop LangGraph instrumentation (opentelemetry-instrumentation-langchain), successful LangGraph executions are incorrectly reported as ERROR.
The issue occurs when consuming graph.astream() with an async for loop and returning once the desired result is produced. During normal async generator shutdown, Python raises GeneratorExit to close the underlying generator. This is expected control flow and does not indicate an application error.
However, the instrumentation catches GeneratorExit as a BaseException and records it as a span failure. As a result, spans such as invoke_agent and, in our environment, the associated workflow span are marked with StatusCode.ERROR even though the workflow completes successfully.
This leads to misleading traces in observability backends like Langfuse, where successful executions appear as failures and can negatively impact debugging, monitoring, and error-rate metrics.
๐ Reproduction steps
- Instrument a LangGraph application using the TraceLoop SDK.
- Execute a graph using graph.astream().
- Consume the stream using an async for loop.
- Return from the loop once the desired output has been produced (instead of consuming the generator to exhaustion).
Example:
async for event in graph.astream(inputs):
if is_final_response(event):
return response - Inspect the generated trace in Langfuse (or another OpenTelemetry backend).
๐ Expected behavior
Successful LangGraph executions should produce successful spans.
Normal async generator shutdown (GeneratorExit) should be treated as expected Python control flow and should not mark spans as ERROR.
๐ Actual Behavior with Screenshots
The instrumentation catches GeneratorExit while wrapping graph.astream() and records it as an application failure.
As a result:
- invoke_agent spans are marked as StatusCode.ERROR.
- Workflow spans are also marked as StatusCode.ERROR.
- The application itself completes successfully and returns the expected response.
๐ค Python Version
Python 3.12
๐ Provide any additional context for the Bug.
The current instrumentation wraps stream()/astream() using except BaseException, which also catches GeneratorExit.
Since GeneratorExit inherits from BaseException (not Exception), normal async generator shutdown is incorrectly treated as an application error.
A possible fix would be to either:
handle GeneratorExit separately and immediately re-raise it without modifying the span, or
catch Exception instead of BaseException, aligning with OpenTelemetry's handling of control-flow exceptions.
In our testing, adding a dedicated GeneratorExit branch before the generic exception handling prevents successful LangGraph executions from being reported as failed while preserving normal error reporting for genuine exceptions.
๐ Have you spent some time to check if this bug has been raised before?
- I checked and didn't find similar issue
Are you willing to submit PR?
Yes I am willing to submit a PR!
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up โ it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Locate the LangChain instrumentation wrapper for stream() and astream(), then inspect the except BaseException handling described in the issue. Reproduce the async-for early return with Python 3.12 and verify that GeneratorExit does not mark spans as ERROR while genuine exceptions still produce error status.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- observability
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100