diff --git a/.changeset/rare-dodos-push.md b/.changeset/rare-dodos-push.md
index 1f312a7d34a..f8154384efa 100644
--- a/.changeset/rare-dodos-push.md
+++ b/.changeset/rare-dodos-push.md
@@ -2,4 +2,4 @@
"@remix-run/server-runtime": patch
---
-Don't log throw redirect response stubs via handleError in Single Fetch
+Don't log thrown response stubs via `handleError` in Single Fetch
diff --git a/integration/single-fetch-test.ts b/integration/single-fetch-test.ts
index 05bbe29d35e..ad0a3cf4fac 100644
--- a/integration/single-fetch-test.ts
+++ b/integration/single-fetch-test.ts
@@ -1687,7 +1687,7 @@ test.describe("single-fetch", () => {
response.headers.set("Location", "/data");
throw response;
}
- export default function Component() {
+ export default function Component() {
return
Should not see me
;
}
`,
@@ -1705,6 +1705,43 @@ test.describe("single-fetch", () => {
expect(errorLogs.length).toBe(0);
});
+ test("does not log thrown non-redirect response stubs via handleError", async () => {
+ let fixture = await createFixture({
+ config: {
+ future: {
+ unstable_singleFetch: true,
+ },
+ },
+ files: {
+ ...files,
+ "app/routes/redirect.tsx": js`
+ export function action({ response }) {
+ response.status = 400;
+ throw response;
+ }
+ export function loader({ response }) {
+ response.status = 400;
+ throw response;
+ }
+ export default function Component() {
+ return Should not see me
;
+ }
+ `,
+ },
+ });
+
+ let errorLogs = [];
+ console.error = (e) => errorLogs.push(e);
+ await fixture.requestDocument("/redirect");
+ expect(errorLogs.length).toBe(1); // ErrorBoundary render logs this
+ await fixture.requestSingleFetchData("/redirect.data");
+ await fixture.requestSingleFetchData("/redirect.data", {
+ method: "post",
+ body: null,
+ });
+ expect(errorLogs.length).toBe(1);
+ });
+
test.describe("client loaders", () => {
test("when no routes have client loaders", async ({ page }) => {
let fixture = await createFixture(
diff --git a/packages/remix-server-runtime/server.ts b/packages/remix-server-runtime/server.ts
index f01f6174eb3..e73f7308fb6 100644
--- a/packages/remix-server-runtime/server.ts
+++ b/packages/remix-server-runtime/server.ts
@@ -40,6 +40,7 @@ import {
getSingleFetchDataStrategy,
getSingleFetchRedirect,
getSingleFetchResourceRouteDataStrategy,
+ isResponseStub,
mergeResponseStubs,
singleFetchAction,
singleFetchLoaders,
@@ -430,7 +431,7 @@ async function handleDocumentRequest(
if (context.errors) {
Object.values(context.errors).forEach((err) => {
// @ts-expect-error `err.error` is "private" from users but intended for internal use
- if (!isRouteErrorResponse(err) || err.error) {
+ if ((!isRouteErrorResponse(err) || err.error) && !isResponseStub(err)) {
handleError(err);
}
});
diff --git a/packages/remix-server-runtime/single-fetch.ts b/packages/remix-server-runtime/single-fetch.ts
index bdfe745f7fc..c9303e79be0 100644
--- a/packages/remix-server-runtime/single-fetch.ts
+++ b/packages/remix-server-runtime/single-fetch.ts
@@ -184,7 +184,7 @@ export async function singleFetchAction(
if (context.errors) {
Object.values(context.errors).forEach((err) => {
// @ts-expect-error This is "private" from users but intended for internal use
- if (!isRouteErrorResponse(err) || err.error) {
+ if ((!isRouteErrorResponse(err) || err.error) && !isResponseStub(err)) {
handleError(err);
}
});
@@ -273,7 +273,7 @@ export async function singleFetchLoaders(
if (context.errors) {
Object.values(context.errors).forEach((err) => {
// @ts-expect-error This is "private" from users but intended for internal use
- if (!isRouteErrorResponse(err) || err.error) {
+ if ((!isRouteErrorResponse(err) || err.error) && !isResponseStub(err)) {
handleError(err);
}
});