Error handling bugfix: pass #1
This commit is contained in:
@@ -185,10 +185,19 @@ export default async function genWebHTML({
|
||||
console.debug = () => {};
|
||||
|
||||
let htmlBody: string;
|
||||
let render_error: any;
|
||||
let has_render_error = false;
|
||||
|
||||
try {
|
||||
const stream = await renderToReadableStream(final_component, {
|
||||
onError(error: any) {
|
||||
if (error.message.includes('unique "key" prop')) return;
|
||||
if (error?.message?.includes('unique "key" prop')) return;
|
||||
|
||||
if (!has_render_error) {
|
||||
has_render_error = true;
|
||||
render_error = error;
|
||||
}
|
||||
|
||||
originalConsole.error(error);
|
||||
},
|
||||
});
|
||||
@@ -198,6 +207,13 @@ export default async function genWebHTML({
|
||||
Object.assign(console, originalConsole);
|
||||
}
|
||||
|
||||
// A component crash during SSR must surface as a proper error (handled
|
||||
// upstream as a 500 page). React only reports these to `onError`; without
|
||||
// rethrowing, the user gets a 200 with broken/blank markup.
|
||||
if (has_render_error) {
|
||||
throw render_error;
|
||||
}
|
||||
|
||||
html += htmlBody;
|
||||
|
||||
return html;
|
||||
|
||||
@@ -11,6 +11,7 @@ import isDevelopment from "../../../utils/is-development";
|
||||
import { existsSync } from "fs";
|
||||
import grabDirNames from "../../../utils/grab-dir-names";
|
||||
import grabRelativePath from "../../../utils/grab-relative-path";
|
||||
import { logBuildErrorsOnce } from "../../../utils/build-error-log";
|
||||
|
||||
const { BUNX_BUNDLER_ERROR_EXIT_FILE, ROOT_DIR } = grabDirNames();
|
||||
|
||||
@@ -28,6 +29,12 @@ type Params = {
|
||||
file_path?: string;
|
||||
debug?: boolean;
|
||||
retry?: boolean;
|
||||
/**
|
||||
* Skip the dev auto-rebuild/retry recovery path. Used by internal callers
|
||||
* (e.g. HMR post-build) where triggering yet another fullRebuild on top
|
||||
* of the rebuild that's already in progress would cascade.
|
||||
*/
|
||||
no_retry?: boolean;
|
||||
return_server_res_only?: boolean;
|
||||
skip_server_res?: boolean;
|
||||
is_hydration?: boolean;
|
||||
@@ -43,6 +50,7 @@ export default async function grabPageComponent(
|
||||
return_server_res_only,
|
||||
skip_server_res,
|
||||
is_hydration,
|
||||
no_retry,
|
||||
} = params;
|
||||
|
||||
const url = req?.url ? new URL(req.url) : undefined;
|
||||
@@ -104,13 +112,21 @@ export default async function grabPageComponent(
|
||||
let retries = 0;
|
||||
const MAX_RETRIES = 2;
|
||||
|
||||
while (retries < MAX_RETRIES) {
|
||||
// Only retry transient failures: if the last rebuild failed
|
||||
// (BUNEXT_BUILD_FAILED), the file is broken and more rebuilds
|
||||
// will just repeat the same error.
|
||||
while (
|
||||
retries < MAX_RETRIES &&
|
||||
!global.BUNEXT_BUILD_FAILED
|
||||
) {
|
||||
await fullRebuild({
|
||||
msg: `Retrying Bundle map for file \`${relative_file_path}\``,
|
||||
});
|
||||
await Bun.sleep(1000);
|
||||
bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
|
||||
bundledMap =
|
||||
global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
|
||||
if (bundledMap?.path) break;
|
||||
retries++;
|
||||
}
|
||||
|
||||
if (!bundledMap?.path) {
|
||||
@@ -170,8 +186,13 @@ export default async function grabPageComponent(
|
||||
error?.name === "NotFoundError" ||
|
||||
error?.status === 404;
|
||||
|
||||
if (!params.retry && is_dev) {
|
||||
while (global.BUNEXT_REBUILD_RETRIES < 2) {
|
||||
if (!params.retry && !params.no_retry && is_dev) {
|
||||
// Skip the retry cascade when the last rebuild failed: the error
|
||||
// is persistent and more rebuilds would repeat it endlessly.
|
||||
while (
|
||||
!global.BUNEXT_BUILD_FAILED &&
|
||||
global.BUNEXT_REBUILD_RETRIES < 2
|
||||
) {
|
||||
global.BUNEXT_REBUILD_RETRIES =
|
||||
global.BUNEXT_REBUILD_RETRIES + 1;
|
||||
|
||||
@@ -198,10 +219,18 @@ export default async function grabPageComponent(
|
||||
if (is404) {
|
||||
global.BUNEXT_IS_404_PAGE = true;
|
||||
} else {
|
||||
log.error(`Error Grabbing Page Component: ${error.message}`);
|
||||
log.error(`Page: ${passed_file_path || url?.pathname}`);
|
||||
logBuildErrorsOnce(
|
||||
[
|
||||
{
|
||||
text: `Error Grabbing Page Component: ${error.message}; Page: ${
|
||||
passed_file_path || url?.pathname
|
||||
}`,
|
||||
},
|
||||
],
|
||||
(line) => log.error(line),
|
||||
);
|
||||
|
||||
if (is_dev) {
|
||||
if (is_dev && !params.no_retry && !global.BUNEXT_BUILD_FAILED) {
|
||||
fullRebuild();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import isDevelopment from "../../../utils/is-development";
|
||||
import { log } from "../../../utils/log";
|
||||
import { logBuildErrorsOnce } from "../../../utils/build-error-log";
|
||||
import getCache from "../../cache/get-cache";
|
||||
import generateWebPageResponseFromComponentReturn from "./generate-web-page-response-from-component-return";
|
||||
import grabPageComponent from "./grab-page-component";
|
||||
@@ -45,7 +46,16 @@ export default async function handleWebPages({
|
||||
...componentRes,
|
||||
});
|
||||
} catch (error: any) {
|
||||
log.error(`Error Handling Web Page: ${error.message}`, error);
|
||||
logBuildErrorsOnce(
|
||||
[
|
||||
{
|
||||
text: `Error Handling Web Page: ${
|
||||
error?.message || String(error)
|
||||
}`,
|
||||
},
|
||||
],
|
||||
(line) => log.error(line),
|
||||
);
|
||||
|
||||
const componentRes = await grabPageErrorComponent({
|
||||
error,
|
||||
|
||||
Reference in New Issue
Block a user