diff --git a/src/browser/tests/worker/worker.html b/src/browser/tests/worker/worker.html
index 5a8562968..29472fa72 100644
--- a/src/browser/tests/worker/worker.html
+++ b/src/browser/tests/worker/worker.html
@@ -553,3 +553,42 @@
});
}
+
+
+
+
diff --git a/src/browser/webapi/SharedWorkerGlobalScope.zig b/src/browser/webapi/SharedWorkerGlobalScope.zig
index b7f3b9cbb..2d07bb5be 100644
--- a/src/browser/webapi/SharedWorkerGlobalScope.zig
+++ b/src/browser/webapi/SharedWorkerGlobalScope.zig
@@ -250,10 +250,14 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
self._http_transfer = null;
self.releaseScriptArena();
- log.err(.browser, "shared worker fetch error", .{
- .url = self._url,
- .err = err,
- });
+ // TransferCanceled is teardown cancelling a still-inflight script fetch,
+ // not a load failure.
+ if (err != error.TransferCanceled) {
+ log.err(.browser, "shared worker fetch error", .{
+ .url = self._url,
+ .err = err,
+ });
+ }
// The worker will never load and onconnect will never be registered.
// Drain the buffered connects so they get dispatched (and dropped at the
diff --git a/src/browser/webapi/Worker.zig b/src/browser/webapi/Worker.zig
index a1c5cd22f..b2cbab0db 100644
--- a/src/browser/webapi/Worker.zig
+++ b/src/browser/webapi/Worker.zig
@@ -268,10 +268,16 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
self._http_transfer = null;
self.releaseScriptArena();
- log.err(.browser, "worker fetch error", .{
- .url = self._url,
- .err = err,
- });
+ // TransferCanceled is not a load failure: terminate() (or worker teardown)
+ // cancelled a still-inflight script fetch. We shouldn't fireErrorEvent
+ // (or bother logging)
+ const canceled = err == error.TransferCanceled;
+ if (!canceled) {
+ log.err(.browser, "worker fetch error", .{
+ .url = self._url,
+ .err = err,
+ });
+ }
// The worker will never load and onmessage will never be registered.
// Drain any buffered messages so they get dispatched (and silently
@@ -280,7 +286,9 @@ fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void {
self._script_loaded = true;
self._worker_scope.drainPendingMessages();
- self.fireErrorEvent(@errorName(err), null);
+ if (!canceled) {
+ self.fireErrorEvent(@errorName(err), null);
+ }
}
fn releaseScriptArena(self: *Worker) void {
@@ -481,7 +489,7 @@ pub const JsApi = struct {
const testing = @import("../../testing.zig");
test "WebApi: Worker" {
- testing.silenceLog(&.{.http});
+ testing.silenceLog(&.{.http, .browser, .browser});
// Worker tests chain a worker-script fetch with a dynamic-import fetch
// and a cross-context postMessage. The default 2 s assertion budget can