What problem does it solve? In a Remix v3 router, throwing a Response (like throw redirect(to)) from a deeply nested helper escapes router.fetch() as a rejected promise and surfaces as a 500, because the router only inspects returned values and has no built-in catch. This forces every helper to return error values that each caller must check. ## Core Features & Use Cases - Thrown Response Recovery: A single catchResponse() middleware catches any Response thrown below it in the chain and returns it as the request's response, preserving status and headers. - Guard-Style Authorization: Helpers typed (): User can redirect signed-out requests by throwing instead of returning null for every call site to check. - Middleware Ordering Guidance: Explains placement relative to session, logging, timing, and compression middleware so Set-Cookie headers and response decoration are not lost when a throw unwinds the chain. - Use Case: A route handler calls an auth helper that throws redirect("/login", { status: 303 }); with catchResponse() installed high in the chain, the client receives the 303 redirect instead of a 500 error. ## Quick Start Add the catchResponse middleware to my Remix v3 router so thrown redirects from helpers return proper responses instead of 500 errors.