feat(vsts): Add API-driven integration setup#113073
feat(vsts): Add API-driven integration setup#113073evanpurkhiser wants to merge 1 commit intomasterfrom
Conversation
| access_token = oauth_data.get("access_token") | ||
| user = get_user_info(access_token) |
There was a problem hiding this comment.
Bug: The code does not validate that access_token exists before using it, leading to an unhandled HTTPError if the token is missing during the VSTS integration setup.
Severity: MEDIUM
Suggested Fix
Before calling get_user_info(), check if access_token is None. If it is, return a graceful error to the user. Alternatively, wrap the get_user_info() call in a try...except HTTPError block to catch the potential exception and handle it gracefully.
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: src/sentry/integrations/vsts/integration.py#L462-L463
Potential issue: In the VSTS integration setup flow, the code defensively handles a
potentially missing `oauth_data` from the pipeline state by defaulting to an empty
dictionary. However, it does not validate the `access_token` retrieved from this data.
If the `access_token` is missing, `None` is passed to `get_user_info()`. This function
then attempts an API call to Azure DevOps with an invalid authorization header (`"bearer
None"`), which results in a 401 response. The subsequent call to
`resp.raise_for_status()` raises an `HTTPError` that is not caught, causing an unhandled
exception and returning a 500 server error to the user, breaking the integration setup
process.
Did we get this right? 👍 / 👎 to inform future reviews.
There was a problem hiding this comment.
Hmm, serializer should ensure this. will follow up
Backend Test FailuresFailures on
|
041107b to
12fdf4e
Compare
Backend Test FailuresFailures on
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 12fdf4e. Configure here.
| ).capture() as lifecycle: | ||
| oauth_data = pipeline.fetch_state("oauth_data") or {} | ||
| access_token = oauth_data.get("access_token") | ||
| user = get_user_info(access_token) |
There was a problem hiding this comment.
Unhandled None access_token crashes get_step_data
Medium Severity
In get_step_data, oauth_data.get("access_token") can return None, which is then passed to get_user_info. That function builds an auth header "bearer None" and calls raise_for_status() on the resulting 401 response, raising an unhandled HTTPError that surfaces as a 500 to the caller. Unlike the legacy AccountConfigView.dispatch (which shares this weakness), this new API-driven path has no intermediary error handling, so the raw exception propagates directly to the API endpoint. A guard or early error return before calling get_user_info would prevent the crash.
Reviewed by Cursor Bugbot for commit 12fdf4e. Configure here.
Add API-driven setup steps for the Azure DevOps (VSTS) integration so it can use the pipeline API instead of the legacy server-rendered flow. Use the shared OAuth API step with the VSTS new identity provider and add a dedicated account selection step for choosing the Azure DevOps organization. Refs [VDY-43: Azure DevOps (VSTS): API-driven integration setup](https://linear.app/getsentry/issue/VDY-43/azure-devops-vsts-api-driven-integration-setup)
12fdf4e to
8322207
Compare
| }, | ||
| ) | ||
| if response.status_code == 200: | ||
| return response.json() |
There was a problem hiding this comment.
Missing JSONDecodeError handling in get_accounts API call
The get_accounts function calls response.json() without catching JSONDecodeError. While the code checks for status_code == 200, Azure DevOps can return 200 with malformed JSON (truncated responses, HTML error pages from CDNs/proxies, or network corruption). This is a documented VSTS bug pattern (SENTRY-5CKF) causing 30,593 events. An unhandled exception during integration setup would surface as a 500 error to users.
Verification
Read the get_accounts function at lines 151-163. Confirmed response.json() is called without try/except. Cross-referenced with references/data-validation.md Example 5 (SENTRY-5CKF) which documents this exact VSTS JSON parsing bug pattern. Also checked references/integration-errors.md which lists 'HTML instead of JSON' and 'Truncated JSON payload' as common VSTS failure modes.
Identified by Warden sentry-backend-bugs · 9JZ-GEY
There was a problem hiding this comment.
Need to look deeper into this to understand what's actually wrong.
|
|
||
|
|
||
| def get_accounts(access_token: str, user_id: str) -> Any | None: | ||
| url = f"https://app.vssps.visualstudio.com/_apis/accounts?memberId={user_id}&api-version=4.1" |
There was a problem hiding this comment.
It feels a little weird that we hardcode this string in this way, rather than having a base string somewhere that we add specific urls onto
There was a problem hiding this comment.
eah this is how this already was lol
| def get_serializer_cls(self) -> type: | ||
| return VstsAccountSelectionSerializer | ||
|
|
||
| def get_step_data(self, pipeline: IntegrationPipeline, request: HttpRequest) -> dict[str, Any]: |
There was a problem hiding this comment.
It could be nice to return a typed dict here since we know exactly what fields we're returning
There was a problem hiding this comment.
Ah yeah this should be typed better


Add API-driven setup steps for the Azure DevOps (VSTS) integration so it
can use the pipeline API instead of the legacy server-rendered flow.
Use the shared OAuth API step with the VSTS new identity provider and
add a dedicated account selection step for choosing the Azure DevOps
organization.
Refs VDY-43: Azure DevOps (VSTS): API-driven integration setup