Error handling bugfix: pass #1

This commit is contained in:
2026-10-02 07:04:22 +01:00
parent 39a5e91eeb
commit d8ea4f8e02
53 changed files with 1179 additions and 410 deletions
@@ -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,