Repository: remix-run/react-router
File: packages/router/router.ts (TypeScript)

Code before:
        await loadLazyRouteModule(match.route, mapRouteProperties, manifest);

        handler = match.route[type];
        if (handler) {
          // Handler still run even if we got interrupted to maintain consistency
          // with un-abortable behavior of handler execution on non-lazy or
          // previously-lazy-loaded routes
          result = await runHandler(handler);
        } else if (type === "action") {
          throw getInternalRouterError(405, {
            method: request.method,
            pathname: new URL(request.url).pathname,
            routeId: match.route.id,
          });
        } else {
          // lazy() route has no loader to run.  Short circuit here so we don't
          // hit the invariant below that errors on returning undefined.
          return { type: ResultType.data, data: undefined };
        }
      }
    } else {
      invariant<Function>(
        handler,
        `Could not find the ${type} to run on the "${match.route.id}" route`
      );

      result = await runHandler(handler);
    }

    invariant(
      result !== undefined,
      `You defined ${type === "action" ? "an action" : "a loader"} for route ` +
        `"${match.route.id}" but didn't return anything from your \`${type}\` ` +
        `function. Please return a value or \`null\`.`
    );
  } catch (e) {
    resultType = ResultType.error;
    result = e;
  } finally {
    if (onReject) {
      request.signal.removeEventListener("abort", onReject);
    }
  }

  if (isResponse(result)) {
    let status = result.status;

    // Process redirects
    if (redirectStatusCodes.has(status)) {
      let location = result.headers.get("Location");
      invariant(

Review comment (suggestion):
Switch to a 404 `ErrorResponse` instead of a raw `Error`

Code after:
        await loadLazyRouteModule(match.route, mapRouteProperties, manifest);

        handler = match.route[type];
        if (handler) {
          // Handler still run even if we got interrupted to maintain consistency
          // with un-abortable behavior of handler execution on non-lazy or
          // previously-lazy-loaded routes
          result = await runHandler(handler);
        } else if (type === "action") {
          let url = new URL(request.url);
          let pathname = url.pathname + url.search;
          throw getInternalRouterError(405, {
            method: request.method,
            pathname,
            routeId: match.route.id,
          });
        } else {
          // lazy() route has no loader to run.  Short circuit here so we don't
          // hit the invariant below that errors on returning undefined.
          return { type: ResultType.data, data: undefined };
        }
      }
    } else if (!handler) {
      let url = new URL(request.url);
      let pathname = url.pathname + url.search;
      throw getInternalRouterError(404, {
        pathname,
      });
    } else {
      result = await runHandler(handler);
    }

    invariant(
      result !== undefined,
      `You defined ${type === "action" ? "an action" : "a loader"} for route ` +
        `"${match.route.id}" but didn't return anything from your \`${type}\` ` +
        `function. Please return a value or \`null\`.`
    );
  } catch (e) {
    resultType = ResultType.error;
    result = e;
  } finally {
    if (onReject) {
      request.signal.removeEventListener("abort", onReject);
    }
  }

  if (isResponse(result)) {
    let status = result.status;

    // Process redirects