Skip to content

feat(seer): Fall back to contextvar ViewerContext for Seer requests#112221

Merged
gricha merged 3 commits intomasterfrom
gricha/feat/seer-viewer-context-fallback
Apr 3, 2026
Merged

feat(seer): Fall back to contextvar ViewerContext for Seer requests#112221
gricha merged 3 commits intomasterfrom
gricha/feat/seer-viewer-context-fallback

Conversation

@gricha
Copy link
Copy Markdown
Member

@gricha gricha commented Apr 3, 2026

make_signed_seer_api_request now auto-reads from the ViewerContext contextvar when no explicit viewer_context parameter is passed. This means Seer calls made during API requests (where the middleware sets the contextvar) automatically get identity context without callers needing to construct and pass SeerViewerContext.

Behavior:

  • Contextvar is the base — includes actor_type and token (kind + scopes) when available
  • Explicit SeerViewerContext param overrides organization_id / user_id if passed
  • If both exist and disagree on non-None values, logs seer.viewer_context_mismatch warning and strips the token from the payload

This is safe to land independently — it's a no-op until the ViewerContext middleware is enabled (viewer-context.enabled option). Existing callers that pass explicit context continue to work unchanged.

Part of the ViewerContext RFC.

When no explicit viewer_context is passed to make_signed_seer_api_request,
read from the contextvar set by ViewerContextMiddleware. If both exist,
explicit fields override but a mismatch logs a warning and strips the
token from the payload as a safety measure.

Also serializes actor_type and token (kind + scopes) when available,
enriching the context Seer receives.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 3, 2026
@gricha gricha marked this pull request as ready for review April 3, 2026 19:48
@gricha gricha requested a review from a team as a code owner April 3, 2026 19:48
Comment thread src/sentry/seer/signed_seer_api.py Outdated

def _resolve_viewer_context(
explicit: SeerViewerContext | None,
) -> dict[str, Any] | None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

is it possible to type the dict more narrowly with e.g. a TypedDict?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

oh that's a good idea, +1



@sentry_sdk.tracing.trace
def make_signed_seer_api_request(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

does it make sense to add any tests to this function based on the viewer context possibilities?

Address review feedback: add tests covering all branches of the
viewer context resolution (contextvar only, explicit only, merge,
mismatch warning, token stripping).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gricha gricha force-pushed the gricha/feat/seer-viewer-context-fallback branch from cd16fea to f6e109e Compare April 3, 2026 20:15
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

result["user_id"] = self.user_id
if self.token is not None:
result["token"] = {"kind": self.token.kind, "scopes": list(self.token.get_scopes())}
return result
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Wire format change breaks existing callers' X-Viewer-Context header

Medium Severity

The X-Viewer-Context header wire format changes for all existing callers even when the contextvar middleware is disabled. Previously, orjson.dumps(viewer_context) serialized SeerViewerContext as {"organization_id": X, "user_id": Y}. Now, orjson.dumps(resolved.serialize()) always includes "actor_type": "unknown" (and potentially "token") in the payload. The PR claims "Existing callers that pass explicit context continue to work unchanged," but the Seer service now receives a different JSON shape for every request that includes viewer context.

Additional Locations (1)
Fix in Cursor Fix in Web

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

Backend Test Failures

Failures on 4af676f in this run:

tests/sentry/event_manager/test_severity.py::TestGetEventSeverity::test_error_event_simplelog
[gw1] linux -- Python 3.13.1 /home/runner/work/sentry/sentry/.venv/bin/python3
tests/sentry/event_manager/test_severity.py:92: in test_error_event_simple
    mock_urlopen.assert_called_with(
/opt/hostedtoolcache/Python/3.13.1/x64/lib/python3.13/unittest/mock.py:977: in assert_called_with
    raise AssertionError(_error_message()) from cause
E   AssertionError: expected call not found.
E   Expected: urlopen('POST', '/v0/issues/severity-score', body=b'{"message":"NopeError: Nopey McNopeface","has_stacktrace":0,"handled":true,"org_id":4557905998970912,"project_id":4557905998970912}', headers={'content-type': 'application/json;charset=utf-8', 'Authorization': 'Rpcsignature rpc0:cd69b91ace0f8185a5956a69c3737c832978c4398d3d8a9a72918be94c1c7527', 'X-Viewer-Context': '{"organization_id":4557905998970912}', 'X-Viewer-Context-Signature': '3b1b2edea105bb87f2a96e59c2b0ecbbd7c9ce82a6982119d5a0b9f7709a4b75'}, timeout=0.2)
E     Actual: urlopen('POST', '/v0/issues/severity-score', body=b'{"message":"NopeError: Nopey McNopeface","has_stacktrace":0,"handled":true,"org_id":4557905998970912,"project_id":4557905998970912}', headers={'content-type': 'application/json;charset=utf-8', 'Authorization': 'Rpcsignature rpc0:cd69b91ace0f8185a5956a69c3737c832978c4398d3d8a9a72918be94c1c7527', 'X-Viewer-Context': '{"actor_type":"unknown","organization_id":4557905998970912}', 'X-Viewer-Context-Signature': 'c38cde7e9a6135c40710e175d36c4e8cbb61356f830028ddd64cd2e2a97f080e'}, timeout=0.2)

ViewerContext.serialize() now always includes actor_type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gricha gricha requested a review from a team as a code owner April 3, 2026 21:26
@gricha gricha merged commit 02db802 into master Apr 3, 2026
69 checks passed
@gricha gricha deleted the gricha/feat/seer-viewer-context-fallback branch April 3, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants