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
21 changes: 7 additions & 14 deletions sentry_sdk/integrations/strawberry.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,34 +181,27 @@
event_processor = _make_request_event_processor(self.execution_context)
scope.add_event_processor(event_processor)

span = sentry_sdk.get_current_span()
if span:
self.graphql_span = span.start_child(
op=op,
name=description,
origin=StrawberryIntegration.origin,
)
else:
self.graphql_span = sentry_sdk.start_span(
op=op,
name=description,
origin=StrawberryIntegration.origin,
)
self.graphql_span = sentry_sdk.start_span(
op=op,
name=description,
origin=StrawberryIntegration.origin,
)
self.graphql_span.__enter__()

self.graphql_span.set_data("graphql.operation.type", operation_type)
self.graphql_span.set_data("graphql.operation.name", self._operation_name)
self.graphql_span.set_data("graphql.document", self.execution_context.query)
self.graphql_span.set_data("graphql.resource_name", self._resource_name)

yield

transaction = self.graphql_span.containing_transaction
if transaction and self.execution_context.operation_name:
transaction.name = self.execution_context.operation_name
transaction.source = TransactionSource.COMPONENT
transaction.op = op

self.graphql_span.finish()
self.graphql_span.__exit__(None, None, None)

Check warning on line 204 in sentry_sdk/integrations/strawberry.py

View check run for this annotation

@sentry/warden / warden: code-review

Span context manager exception handling is bypassed

The manual `__enter__()`/`__exit__()` pattern in a generator does not handle exceptions properly. If an exception occurs during the GraphQL operation (at `yield`), the code after `yield` including `__exit__()` never executes, leaving the span unclosed. Additionally, `__exit__(None, None, None)` is always called even if the operation failed, so the span's built-in error detection (which sets `SPANSTATUS.INTERNAL_ERROR` when exception info is passed) is bypassed. This results in spans that leak on errors and never report failure status.

Check warning on line 204 in sentry_sdk/integrations/strawberry.py

View check run for this annotation

@sentry/warden / warden: find-bugs

Span __exit__ always called with None, losing exception information

The manual call to `self.graphql_span.__exit__(None, None, None)` always passes `None` for exception type, value, and traceback. The span's `__exit__` method (in tracing.py:399-400) checks these values to set the span status to INTERNAL_ERROR when an exception occurs. By always passing `None`, any exceptions that occur during the GraphQL operation will not be reflected in the span status, making failed operations appear successful in Sentry traces.

Check warning on line 204 in sentry_sdk/integrations/strawberry.py

View workflow job for this annotation

GitHub Actions / warden: find-bugs

Span context manager not properly closed on exception - span leaks and scope corruption

The new code calls `self.graphql_span.__enter__()` at line 189 and `self.graphql_span.__exit__(None, None, None)` at line 204, but if an exception is thrown into the generator at the `yield` statement (line 196), the code after `yield` never executes. This leaves the span unfinished and corrupts the scope because `__exit__` restores the old span. Additionally, passing `(None, None, None)` to `__exit__` prevents the span from being marked with `INTERNAL_ERROR` status when an exception occurs.
Copy link

Choose a reason for hiding this comment

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

Missing try/finally around yield leaks scope state

Medium Severity

__enter__() modifies scope.span and stores the old span in _context_manager_state, but the yield between __enter__() and __exit__() is not wrapped in a try/finally. If the Strawberry framework throws an exception into the generator or calls close() on it, the code after yield (including __exit__) is never reached. This leaves scope.span permanently pointing to the now-stale graphql_span, and the previous span is never restored. The old code using finish() didn't have this issue because it never called __enter__() and therefore never modified scope state.

Fix in Cursor Fix in Web


def on_validate(self) -> "Generator[None, None, None]":
self.validation_span = self.graphql_span.start_child(
Expand Down
Loading