Skip to content

feat(api): Add sentryCellFetch with injectable error handling#116114

Draft
ryan953 wants to merge 6 commits into
masterfrom
ryan953/ref-requestPromise
Draft

feat(api): Add sentryCellFetch with injectable error handling#116114
ryan953 wants to merge 6 commits into
masterfrom
ryan953/ref-requestPromise

Conversation

@ryan953
Copy link
Copy Markdown
Member

@ryan953 ryan953 commented May 23, 2026

Summary

  • Adds sentryCellFetch — a new fetch function that owns the fetch() call directly and accepts injectable error handlers via configureSentryCellFetch(), replacing the baked-in side effects in Client.request() (sudo modal, auth redirects, project-renamed redirects).
  • Extracts fetchWithUrl helper so callers can fetch by URL string without constructing a full QueryFunctionContext.
  • Adds sentryCellFetchErrorHandlers.tsx with factory functions (createDefaultAuthErrorHandler, createDefaultSudoHandler, createDefaultProjectRenamedHandler) that replicate the existing behavior from api.tsx but are testable and overridable.
  • Wires up configureSentryCellFetch in both app entrypoints (main.tsx, gsAdmin/init.tsx) alongside existing setApiNavigate calls.
  • Includes a Jest mock (__mocks__/sentryCellFetch.tsx) that delegates to MockApiClient so existing test patterns work unchanged.

Test plan

  • sentryCellFetch.spec.tsx — 17 tests covering success responses, error responses, request construction, error handler dispatch, and CSRF/credentials
  • sentryCellFetchErrorHandlers.spec.tsx — 17 tests covering sudo modal, auth redirects, project-renamed, and edge cases
  • fetchParity.spec.tsx — 38 tests running the same scenarios against both apiFetch (old path) and sentryCellFetch (new path) to verify identical behavior for success, errors, request construction, and error handler side effects
  • Typecheck passes (pnpm run typecheck)
  • Lint passes (pnpm run lint:js)

ryan953 added 5 commits May 23, 2026 07:53
New fetch implementation that calls fetch() directly instead of going
through Client.request(), with all UI/navigation error handling
extracted into configurable callbacks (onAuthError, onSudoRequired,
onProjectRenamed, onError). Includes factory functions that replicate
the existing error handling logic, a Jest mock backed by MockApiClient,
and comprehensive tests for both the fetch layer and error handlers.
Both sentryCellFetch and sentryCellFetchInfinite now delegate to a
shared fetchWithUrl that accepts a plain url string instead of a
query key, reducing duplication and enabling direct use outside of
TanStack Query contexts.
Verifies that both the old path (apiFetch → Client.requestPromise) and
the new path (sentryCellFetch → fetch directly) produce identical results
for success responses, error responses, request construction, and error
handler side effects (sudo, project-moved, auth 401).
Configure sentryCellFetch with default error handlers (sudo, auth,
project-renamed) in both app entrypoints alongside existing
setApiNavigate calls, sharing the same navigate instance.
@ryan953 ryan953 requested a review from a team as a code owner May 23, 2026 16:17
@github-actions github-actions Bot added the Scope: Frontend Automatically applied to PRs that change frontend components label May 23, 2026
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 2 potential issues.

Fix All in Cursor

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

Reviewed by Cursor Bugbot for commit 139b580. Configure here.


// 4. Generic catch-all
if (errorHandlers?.onError?.(responseMeta, options)) {
return {headers: buildResponseHeaders(response), json: undefined as unknown};
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.

Suppressed errors resolve as success

Medium Severity

When onAuthError, onProjectRenamed, or onError suppress a failed response, handleErrorResponse resolves with { json: undefined } instead of rejecting or staying pending. Used as a React Query queryFn, that marks the query successful and can cache undefined, skip retries, and render empty “loaded” UI if navigation is delayed or incomplete.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 139b580. Configure here.

) {
return errorHandlers.onSudoRequired(responseMeta, () =>
executeFetch(fullUrl, options, undefined)
);
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.

Sudo retry ignores abort signal

Low Severity

The sudo retry callback re-invokes executeFetch with signal set to undefined, so a React Query cancellation on the original context.signal does not abort the retried request after the sudo modal completes.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 139b580. Configure here.

}

if (!isDemoModeActive()) {
Cookies.set('session_expired', '1');
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.

High severity and reachable issue identified in your code:
Line 66 has a vulnerable usage of js-cookie, introducing a high severity vulnerability.

ℹ️ Why this is reachable

A reachable issue is a real security risk because your project actually executes the vulnerable code. This issue is reachable because your code uses a certain version of js-cookie.
Affected versions of js-cookie are vulnerable to Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution'). The internal assign() helper in js-cookie walks attribute objects with a for..in loop and writes properties directly onto the target, which lets a __proto__ key from a JSON-parsed source trigger the Object.prototype.__proto__ setter. An attacker who controls any attributes object passed to set, remove, withAttributes, or withConverter can inject cookie attributes (domain, path, secure, samesite, expires) and pull off session fixation or downgrade Secure/SameSite protections.

References: GHSA

To resolve this comment:
Upgrade this dependency to at least version 3.0.7 at pnpm-lock.yaml.

💬 Ignore this finding

To ignore this, reply with:

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

You can view more details on this finding in the Semgrep AppSec Platform here.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

📊 Type Coverage Diff

Metric Before After Delta
Coverage 93.57% 93.57% ±0%
Typed 133,210 133,283 🟢 +73
Untyped 9,147 9,159 🔴 +12
🔍 12 new type safety issues introduced

any-typed symbols (8 new)

File Line Detail
static/app/utils/api/sentryCellFetch.tsx 142 responseJSON (var)
static/app/utils/api/sentryCellFetch.tsx 185 responseData (var)
static/app/utils/api/sentryCellFetch.tsx 199 code (var)
static/app/utils/api/sentryCellFetchErrorHandlers.tsx 48 code (var)
static/app/utils/api/sentryCellFetchErrorHandlers.tsx 49 extra (var)
static/app/utils/api/sentryCellFetchErrorHandlers.tsx 82 code (var)
static/app/utils/api/sentryCellFetchErrorHandlers.tsx 84 reject (param)
static/app/utils/api/sentryCellFetchErrorHandlers.tsx 115 slug (var)

Type assertions (as) (4 new)

File Line Detail
static/app/utils/api/sentryCellFetch.tsx 214 as unknownundefined as unknown
static/app/utils/api/sentryCellFetch.tsx 222 as unknownundefined as unknown
static/app/utils/api/sentryCellFetch.tsx 227 as unknownundefined as unknown
static/app/utils/api/sentryCellFetch.tsx 259 as ApiResponse<TQueryFnData>result as ApiResponse<TQueryFnData>

This is informational only and does not block the PR.

body = JSON.stringify(options.data);
}

const baseHeaders = currentConfig.headers ?? JSON_HEADERS;
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.

Bug: Calling configureSentryCellFetch with custom headers replaces the default JSON_HEADERS instead of merging them, which can silently drop required Content-Type and Accept headers.
Severity: LOW

Suggested Fix

The configureSentryCellFetch function should merge the provided newConfig.headers with the default JSON_HEADERS instead of replacing them. Alternatively, add a development-time warning or documentation to clarify that callers must include all required JSON headers when overriding.

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: static/app/utils/api/sentryCellFetch.tsx#L126

Potential issue: The `configureSentryCellFetch` function allows custom headers to
completely replace the default `JSON_HEADERS`. If a developer configures it with partial
headers (e.g., only an `Authorization` header), the `Content-Type` and `Accept` headers
required for JSON API communication will be silently dropped. This happens because
`currentConfig.headers` is used in place of `JSON_HEADERS` if it's provided, rather than
being merged. While current code does not trigger this, it is a latent design issue that
could lead to broken API requests in the future.

Did we get this right? 👍 / 👎 to inform future reviews.

@ryan953 ryan953 marked this pull request as draft May 23, 2026 16:22
…lysis

Document how Client.clear()/cancel() patterns (unmount cleanup, stale
search cancellation, stale data fetch) are handled natively by TanStack
Query via AbortSignal, and why the parity test intentionally skips this.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Scope: Frontend Automatically applied to PRs that change frontend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant