-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Closed
Closed
Copy link
Description
I'm using React Router as a...
framework
Reproduction
https://stackblitz.com/edit/github-8jsa5pu8?file=app%2Froot.tsx
System Info
System:
OS: macOS 15.5
CPU: (10) arm64 Apple M1 Pro
Memory: 84.63 MB / 32.00 GB
Shell: 3.7.1 - /opt/homebrew/bin/fish
Binaries:
Node: 20.11.0 - ~/.nvm/versions/node/v20.11.0/bin/node
npm: 10.2.4 - ~/.nvm/versions/node/v20.11.0/bin/npm
pnpm: 10.11.0 - /opt/homebrew/bin/pnpm
Watchman: 2025.05.26.00 - /opt/homebrew/bin/watchman
Browsers:
Chrome: 137.0.7151.69
Safari: 18.5
npmPackages:
@react-router/dev: ^7.5.3 => 7.6.2
@react-router/node: ^7.5.3 => 7.6.2
@react-router/serve: ^7.5.3 => 7.6.2
react-router: ^7.5.3 => 7.6.2
vite: ^6.3.3 => 6.3.5Used Package Manager
npm
Expected Behavior
When middleware throws, earlier middleware that has already awaited next() should still be able to run its after-logic (e.g. modifying the response). For example, if a header is set after await next(), it should be present in the final response, even if later middleware throws.
Actual Behavior
If a later middleware throws, earlier middleware that already awaited next() doesn’t get a chance to run its after-logic. This makes it impossible to reliably apply global headers, logging or cleanup etc.
export const unstable_middleware: Route.unstable_MiddlewareFunction[] = [
async (_, next) => {
const response = await next();
if (!response.headers.has('Cache-Control')) {
response.headers.set(
'Cache-Control',
'no-store, no-cache, must-revalidate, private'
);
}
return response;
},
async () => {
throw new Error(
"Throwing here shouldn't prevent earlier middleware from unwinding!"
);
// EXPECT: 500 response with Cache-Control header
// ACTUAL: 500 response without Cache-Control header
},
];NOTE: Throwing in the loader or <Component /> works as expected; the Cache-Control header is set.