Skip to content

Commit 03d6cf2

Browse files
authored
Fix bubbling of errors thrown from unstable_patchRoutesOnMiss (#11786)
1 parent 6def427 commit 03d6cf2

File tree

3 files changed

+85
-24
lines changed

3 files changed

+85
-24
lines changed

.changeset/clever-emus-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Fix bubbling of errors thrown from `unstable_patchRoutesOnMiss`

packages/router/__tests__/lazy-discovery-test.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,7 +1413,7 @@ describe("Lazy Route Discovery (Fog of War)", () => {
14131413
]);
14141414
});
14151415

1416-
it("handles errors thrown from children() (GET navigation)", async () => {
1416+
it("handles errors thrown from patchRoutesOnMiss() (GET navigation)", async () => {
14171417
let shouldThrow = true;
14181418
router = createRouter({
14191419
history: createMemoryHistory(),
@@ -1455,7 +1455,8 @@ describe("Lazy Route Discovery (Fog of War)", () => {
14551455
400,
14561456
"Bad Request",
14571457
new Error(
1458-
'Unable to match URL "/a/b" - the `children()` function for route `a` threw the following error:\nError: broke!'
1458+
'Unable to match URL "/a/b" - the `unstable_patchRoutesOnMiss()` ' +
1459+
"function threw the following error:\nError: broke!"
14591460
),
14601461
true
14611462
),
@@ -1484,7 +1485,7 @@ describe("Lazy Route Discovery (Fog of War)", () => {
14841485
expect(router.state.matches.map((m) => m.route.id)).toEqual(["a", "b"]);
14851486
});
14861487

1487-
it("handles errors thrown from children() (POST navigation)", async () => {
1488+
it("handles errors thrown from patchRoutesOnMiss() (POST navigation)", async () => {
14881489
let shouldThrow = true;
14891490
router = createRouter({
14901491
history: createMemoryHistory(),
@@ -1529,7 +1530,8 @@ describe("Lazy Route Discovery (Fog of War)", () => {
15291530
400,
15301531
"Bad Request",
15311532
new Error(
1532-
'Unable to match URL "/a/b" - the `children()` function for route `a` threw the following error:\nError: broke!'
1533+
'Unable to match URL "/a/b" - the `unstable_patchRoutesOnMiss()` ' +
1534+
"function threw the following error:\nError: broke!"
15331535
),
15341536
true
15351537
),
@@ -1560,6 +1562,61 @@ describe("Lazy Route Discovery (Fog of War)", () => {
15601562
});
15611563
expect(router.state.matches.map((m) => m.route.id)).toEqual(["a", "b"]);
15621564
});
1565+
1566+
it("bubbles errors thrown from patchRoutesOnMiss() during hydration", async () => {
1567+
router = createRouter({
1568+
history: createMemoryHistory({
1569+
initialEntries: ["/parent/child/grandchild"],
1570+
}),
1571+
routes: [
1572+
{
1573+
id: "parent",
1574+
path: "parent",
1575+
hasErrorBoundary: true,
1576+
children: [
1577+
{
1578+
id: "child",
1579+
path: "child",
1580+
},
1581+
],
1582+
},
1583+
],
1584+
async unstable_patchRoutesOnMiss() {
1585+
await tick();
1586+
throw new Error("broke!");
1587+
},
1588+
}).initialize();
1589+
1590+
expect(router.state).toMatchObject({
1591+
location: { pathname: "/parent/child/grandchild" },
1592+
initialized: false,
1593+
errors: null,
1594+
});
1595+
expect(router.state.matches.length).toBe(0);
1596+
1597+
await tick();
1598+
expect(router.state).toMatchObject({
1599+
location: { pathname: "/parent/child/grandchild" },
1600+
actionData: null,
1601+
loaderData: {},
1602+
errors: {
1603+
parent: new ErrorResponseImpl(
1604+
400,
1605+
"Bad Request",
1606+
new Error(
1607+
'Unable to match URL "/parent/child/grandchild" - the ' +
1608+
"`unstable_patchRoutesOnMiss()` function threw the following " +
1609+
"error:\nError: broke!"
1610+
),
1611+
true
1612+
),
1613+
},
1614+
});
1615+
expect(router.state.matches.map((m) => m.route.id)).toEqual([
1616+
"parent",
1617+
"child",
1618+
]);
1619+
});
15631620
});
15641621

15651622
describe("fetchers", () => {

packages/router/router.ts

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1680,14 +1680,14 @@ export function createRouter(init: RouterInit): Router {
16801680
if (discoverResult.type === "aborted") {
16811681
return { shortCircuited: true };
16821682
} else if (discoverResult.type === "error") {
1683-
let { error, notFoundMatches, route } = handleDiscoverRouteError(
1683+
let { boundaryId, error } = handleDiscoverRouteError(
16841684
location.pathname,
16851685
discoverResult
16861686
);
16871687
return {
1688-
matches: notFoundMatches,
1688+
matches: discoverResult.partialMatches,
16891689
pendingActionResult: [
1690-
route.id,
1690+
boundaryId,
16911691
{
16921692
type: ResultType.error,
16931693
error,
@@ -1856,15 +1856,15 @@ export function createRouter(init: RouterInit): Router {
18561856
if (discoverResult.type === "aborted") {
18571857
return { shortCircuited: true };
18581858
} else if (discoverResult.type === "error") {
1859-
let { error, notFoundMatches, route } = handleDiscoverRouteError(
1859+
let { boundaryId, error } = handleDiscoverRouteError(
18601860
location.pathname,
18611861
discoverResult
18621862
);
18631863
return {
1864-
matches: notFoundMatches,
1864+
matches: discoverResult.partialMatches,
18651865
loaderData: {},
18661866
errors: {
1867-
[route.id]: error,
1867+
[boundaryId]: error,
18681868
},
18691869
};
18701870
} else if (!discoverResult.matches) {
@@ -3064,18 +3064,17 @@ export function createRouter(init: RouterInit): Router {
30643064
pathname: string,
30653065
discoverResult: DiscoverRoutesErrorResult
30663066
) {
3067-
let matches = discoverResult.partialMatches;
3068-
let route = matches[matches.length - 1].route;
3069-
let error = getInternalRouterError(400, {
3070-
type: "route-discovery",
3071-
routeId: route.id,
3072-
pathname,
3073-
message:
3074-
discoverResult.error != null && "message" in discoverResult.error
3075-
? discoverResult.error
3076-
: String(discoverResult.error),
3077-
});
3078-
return { notFoundMatches: matches, route, error };
3067+
return {
3068+
boundaryId: findNearestBoundary(discoverResult.partialMatches).route.id,
3069+
error: getInternalRouterError(400, {
3070+
type: "route-discovery",
3071+
pathname,
3072+
message:
3073+
discoverResult.error != null && "message" in discoverResult.error
3074+
? discoverResult.error
3075+
: String(discoverResult.error),
3076+
}),
3077+
};
30793078
}
30803079

30813080
function cancelActiveDeferreds(
@@ -5364,8 +5363,8 @@ function getInternalRouterError(
53645363
statusText = "Bad Request";
53655364
if (type === "route-discovery") {
53665365
errorMessage =
5367-
`Unable to match URL "${pathname}" - the \`children()\` function for ` +
5368-
`route \`${routeId}\` threw the following error:\n${message}`;
5366+
`Unable to match URL "${pathname}" - the \`unstable_patchRoutesOnMiss()\` ` +
5367+
`function threw the following error:\n${message}`;
53695368
} else if (method && pathname && routeId) {
53705369
errorMessage =
53715370
`You made a ${method} request to "${pathname}" but ` +

0 commit comments

Comments
 (0)