Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
6dd45711c4 Fix HTMLRewriter crash when element handlers throw with Bun.file()
Previously, when using HTMLRewriter with Bun.file() and an element handler
threw a JavaScript exception, it would cause an assertion failure in JSC's
ExceptionScope::releaseAssertNoException(), leading to a SIGABRT crash.

The issue occurred because JavaScript exceptions thrown in element handler
callbacks weren't being properly captured and handled, leaving them
uncaught in the JSC exception scope.

This fix:
- Properly captures JavaScript exceptions using tryTakeException()
- Stores them in the unhandled rejection capture mechanism
- Ensures exceptions are retrieved and thrown by createLOLHTMLError
- Prevents assertion failures while maintaining existing error handling

The fix works for both synchronous and asynchronous processing since
BufferOutputSink.init sets up the capture mechanism in both cases.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 23:24:03 +00:00
2 changed files with 54 additions and 1 deletions

View File

@@ -891,7 +891,19 @@ fn HandlerCallback(
JSValue.zero,
&.{wrapper.toJS(this.global)},
) catch {
// If there's an error, we'll propagate it to the caller.
// If there's an error, we need to properly handle the JavaScript exception
// to prevent it from causing assertion failures later.
// Store the exception for later retrieval by createLOLHTMLError
if (this.global.tryTakeException()) |exception| {
if (this.global.bunVM().unhandled_pending_rejection_to_capture) |err_ptr| {
exception.ensureStillAlive();
exception.protect();
err_ptr.* = exception;
}
// Note: The capture mechanism is set up for both synchronous and
// asynchronous processing, so the exception will be properly
// retrieved and thrown by createLOLHTMLError.
}
return true;
};

View File

@@ -0,0 +1,41 @@
import { test, expect } from "bun:test";
import { tempDirWithFiles } from "harness";
import { join } from "path";
test("HTMLRewriter should not crash with Bun.file() and element handler error", async () => {
// Create a temporary HTML file
const dir = tempDirWithFiles("htmlrewriter-crash", {
"min.html": "<script></script>",
});
const filePath = join(dir, "min.html");
// This should not crash the process. The error handling varies between
// synchronous and asynchronous processing, but it should never crash.
let didNotCrash = false;
try {
const rewriter = new HTMLRewriter().on("script", {
element(a) {
throw new Error("abc");
},
});
const response = rewriter.transform(new Response(Bun.file(filePath)));
// For file inputs, the processing is asynchronous, so errors may not
// be thrown synchronously but should be handled gracefully
if (response) {
try {
await response.text();
} catch (error) {
// Expected to possibly throw an error during async processing
}
}
didNotCrash = true;
} catch (error) {
// Any error here is fine as long as it doesn't crash the process
didNotCrash = true;
}
// The main assertion is that we reach this point without crashing
expect(didNotCrash).toBe(true);
});