mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
fix(ui): stop reporting cancelled queries to Sentry (#20430)
React Query's CancelledError keeps the default Error name, so the name-based abort check never matched it. A query cancelled with revert: false (for example by the persisted cache when an issue read is denied or the account changes) reached the query cache onError as an "unknown" error and was sent to Sentry. Recognize it by class. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
18697b8d21
commit
5570debc5b
3 files changed
+18
-2
No files matched your search
@@ -1,3 +1,4 @@
|
||||
import { CancelledError } from "@tanstack/react-query";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { ApiError } from "./api-error";
|
||||
import {
|
||||
@@ -37,6 +38,7 @@ describe("classifyError", () => {
|
||||
["422", apiError(422), "invalid"],
|
||||
["500", apiError(500, { error: "boom" }), "unknown"],
|
||||
["an abort", new DOMException("Aborted", "AbortError"), "aborted"],
|
||||
["a React Query cancellation", new CancelledError({ revert: false }), "aborted"],
|
||||
["a programming TypeError", new TypeError("Cannot read properties of undefined"), "unknown"],
|
||||
["a plain Error", new Error("nope"), "unknown"],
|
||||
["undefined", undefined, "unknown"],
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
* by hand-rolled fetches (health, uploads) classify the same way.
|
||||
*/
|
||||
|
||||
import { CancelledError } from "@tanstack/react-query";
|
||||
|
||||
export type ErrorKind =
|
||||
| "transient"
|
||||
| "auth"
|
||||
@@ -181,8 +183,9 @@ function readableServerMessage(err: unknown): string | null {
|
||||
// --- Classification ---------------------------------------------------------------
|
||||
|
||||
function isAbortError(err: unknown): boolean {
|
||||
const name = record(err)?.name;
|
||||
return name === "AbortError" || name === "CancelledError";
|
||||
// React Query's `CancelledError` keeps the default `name` ("Error"), so it
|
||||
// can only be recognized by class.
|
||||
return record(err)?.name === "AbortError" || err instanceof CancelledError;
|
||||
}
|
||||
|
||||
function isNetworkTypeError(err: unknown): boolean {
|
||||
|
||||
@@ -278,6 +278,17 @@ describe("createAppQueryClient", () => {
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it("does not report a query that was cancelled without reverting", async () => {
|
||||
const queryFn = vi.fn(() => new Promise<string>(() => {}));
|
||||
const observer = new QueryObserver(client, { queryKey: ["issues", "detail", "PAP-1"], queryFn });
|
||||
const unsubscribe = observer.subscribe(() => {});
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
await client.cancelQueries({ queryKey: ["issues", "detail"] }, { revert: false });
|
||||
expect(observer.getCurrentResult().error?.message).toBe("CancelledError");
|
||||
expect(reportError).not.toHaveBeenCalled();
|
||||
unsubscribe();
|
||||
});
|
||||
|
||||
it("leaves a cache-only alias stale instead of refetching it without a queryFn", async () => {
|
||||
client.setQueryData(["issues", "detail", "issue-uuid"], { id: "issue-uuid", title: "Seeded" });
|
||||
const queryFn = vi.fn(async () => "fetched");
|
||||
|
||||
Reference in new issue
Block a user