Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
f8734c7
process: reject partial property descriptors on process.env
cirospaciari Jul 18, 2026
5c8028f
test: quarantine worker-terminate ASAN crashes, matching main
cirospaciari Jul 20, 2026
7ef097b
worker: hold an exception scope while populating process.env
cirospaciari Jul 20, 2026
19227b9
worker: validate execArgv against a real flag policy table (+2 tests)
cirospaciari Jul 22, 2026
608e06c
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 22, 2026
6c0412a
worker: allow Bun run-surface flags in execArgv and NODE_OPTIONS
cirospaciari Jul 22, 2026
4d05af1
ci: keep the binary size allowance on the stack tip [allow size]
cirospaciari Jul 22, 2026
a2c7d8d
Merge ciro/worker-threads-node-tests into claude/process-env-descript…
cirospaciari Jul 22, 2026
6a6c7d2
ci: keep the binary size allowance on the stack tip [allow size]
cirospaciari Jul 22, 2026
2fdb75a
worker: take the JSLock before installing globalThis.gc
cirospaciari Jul 22, 2026
54baff8
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 22, 2026
52f6889
worker: cover the full process.execArgv surface in the execArgv policy
cirospaciari Jul 22, 2026
7f26d4d
ci: rebuild [allow size]
cirospaciari Jul 23, 2026
0bd590d
worker: split glued short-flag values in execArgv like the CLI parser
cirospaciari Jul 23, 2026
b07212e
worker/env: chain short execArgv flags like the CLI parser; no phanto…
cirospaciari Jul 23, 2026
7ce8a68
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 23, 2026
5c7c5b3
worker: chain short flags in NODE_OPTIONS validation too
cirospaciari Jul 23, 2026
4698960
process.env: reject valueless data descriptors and symbol keys like n…
cirospaciari Jul 23, 2026
5798e87
process: normalize glued short flags when building process.execArgv
cirospaciari Jul 23, 2026
11bb858
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 23, 2026
e2ba2d4
process: scope execArgv short-flag normalization to the bun/node entr…
cirospaciari Jul 23, 2026
12b617a
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 23, 2026
31b3953
Delete flaky GC-observation node tests (#35182)
Jarred-Sumner Jul 22, 2026
c545cec
process: close the -br/run gaps in execArgv short-flag normalization
robobun Jul 24, 2026
f359442
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 24, 2026
5e87cc7
clippy: use ? now that push_normalized_short_token returns Option
robobun Jul 24, 2026
d1b410e
worker: share the process.execArgv token builder with the inherit-pat…
robobun Jul 24, 2026
ebcb030
worker: honor prev_takes_value before short-chain normalization; acce…
robobun Jul 24, 2026
6068f57
test: pin the -pe execArgv round-trip
cirospaciari Jul 24, 2026
b1010fb
process.env: stringify defineProperty values like node; isolate SHARE…
robobun Jul 24, 2026
490b699
process.env: coerce defined values to strings; fail preventExtensions…
cirospaciari Jul 24, 2026
f9ac6f4
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 24, 2026
7a8bf0c
worker: only One/Many flags consume the next argv token
cirospaciari Jul 24, 2026
12d92a7
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 24, 2026
4f1552f
windowsEnv: fix defineProperty bookkeeping for always-present accesso…
cirospaciari Jul 24, 2026
1fce00b
test: pin windowsEnv defineProperty bookkeeping for always-present ac…
robobun Jul 24, 2026
01d9f03
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 24, 2026
e8a82b8
test: drop stale bookkeeping for the GC-observation tests deleted by …
robobun Jul 24, 2026
cec7675
test: run the windowsEnv special-accessor defineProperty check in a p…
robobun Jul 24, 2026
f38b64b
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 24, 2026
409d44d
test: drop the orphaned comment block for the deleted tls memleak entry
robobun Jul 24, 2026
42233c1
Merge remote-tracking branch 'origin/ciro/worker-threads-node-tests' …
robobun Aug 3, 2026
d7b446a
worker: use AUTO_PARAMS directly in table_map after #36184 made the s…
robobun Aug 3, 2026
bdfa422
Worker.cpp: guard WebWorker__dispatchError/dispatchErrorWithValue aga…
robobun Aug 3, 2026
cb25e5d
web_worker: clear the pending TerminationException (not just the requ…
robobun Aug 3, 2026
9f100bf
trim comments to <=3 lines, cite spec/node source
robobun Aug 3, 2026
4da9c57
Revert the shutdown clear_termination_exception swap
robobun Aug 3, 2026
9c8c3a4
Merge remote-tracking branch 'origin/ciro/worker-threads-node-tests' …
robobun Aug 3, 2026
462ca19
Merge remote-tracking branch 'origin/ciro/worker-threads-node-tests' …
robobun Aug 3, 2026
e623e6c
Merge remote-tracking branch 'origin/ciro/worker-threads-node-tests' …
robobun Aug 4, 2026
af68afa
clippy: SAFETY comment for us_loop_idle_ns raw call from 388af0e9
robobun Aug 4, 2026
1ade7f3
Merge remote-tracking branch 'origin/ciro/worker-threads-node-tests' …
robobun Aug 4, 2026
21c5d62
Merge branch 'main' into claude/process-env-descriptor-validation
cirospaciari Aug 5, 2026
bd9ab18
Merge branch 'ciro/worker-threads-node-tests' into claude/process-env…
cirospaciari Aug 5, 2026
3bb4d69
Merge branch 'main' into claude/process-env-descriptor-validation
robobun Aug 7, 2026
e75f5ab
Merge branch 'ciro/worker-threads-node-tests' into claude/process-env…
cirospaciari Aug 7, 2026
451b70a
worker_exec_argv: route the = split through strings::index_of_char_usize
cirospaciari Aug 7, 2026
846e516
windows: wrap a worker's env snapshot in the windowsEnv proxy
robobun Aug 7, 2026
8ce6a4c
process.env: refuse preventExtensions on the main-thread map and the …
robobun Aug 7, 2026
dae59d4
retitle the Windows accessor test for the post-merge reject semantics…
robobun Aug 7, 2026
80485f4
Merge branch 'ciro/worker-threads-node-tests' into claude/process-env…
cirospaciari Aug 7, 2026
80f9d48
Merge branch 'ciro/worker-threads-node-tests' into claude/process-env…
cirospaciari Aug 7, 2026
945f6c3
env maps: align symbol-key handling across put/defineProperty and pla…
robobun Aug 7, 2026
c198cfe
Trim comments to node-source/spec references
robobun Aug 7, 2026
18dfe4a
Merge branch 'ciro/worker-threads-node-tests' into claude/process-env…
robobun Aug 7, 2026
bb2f2c4
NODE_OPTIONS: consume a required flag's next token unconditionally
robobun Aug 8, 2026
c52ec62
env: match node's Symbol conversion message on the regular map's put …
robobun Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions src/js/builtins/ProcessObjectInternals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -548,11 +548,6 @@ export function windowsEnv(
return delete internalEnv[k];
},
defineProperty(_, p, attributes) {
// String(symbol) does not throw (it returns the descriptive string), so
// reject symbol keys explicitly like the set trap does.
if (typeof p === "symbol") {
throw new TypeError("Cannot convert a Symbol value to a string");
}
// Same validation as JSEnvironmentVariableMap::defineOwnProperty on
// POSIX: only plain, fully-permissive data descriptors are accepted.
if ("get" in attributes || "set" in attributes) {
Expand All @@ -572,6 +567,12 @@ export function windowsEnv(
"'process.env' only accepts a configurable, writable, and enumerable data descriptor",
);
}
// Node coerces the key only after the descriptor validates; String(symbol)
// does not throw (it returns the descriptive string), so reject symbol
// keys explicitly like the set trap does.
if (typeof p === "symbol") {
throw new TypeError("Cannot convert a Symbol value to a string");
}
if (typeof attributes.value === "symbol") {
throw new TypeError("Cannot convert a Symbol value to a string");
}
Expand All @@ -597,6 +598,9 @@ export function windowsEnv(
// .slice() because paranoia that there is a way to call this without the engine cloning it for us
return envMapList.slice();
},
preventExtensions() {
return false;
},
});
}

Expand Down
16 changes: 12 additions & 4 deletions src/jsc/VirtualMachine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1696,12 +1696,15 @@ pub type RuntimeState = *mut c_void;

/// The subset of a Worker's `execArgv` that bun acts on. Flags whose value must
/// outlive the parse (--cpu-prof-dir/-name) are absent; nothing needs them yet.
#[derive(Default, Clone, Copy)]
#[derive(Default, Clone)]
pub struct WorkerExecArgv {
pub allow_addons: Option<bool>,
pub use_system_ca: Option<bool>,
pub cpu_prof: bool,
pub cpu_prof_interval: Option<u32>,
pub expose_gc: bool,
/// `--require`/`-r`/`--preload`/`--import` specifiers, in order.
pub preloads: Vec<Box<[u8]>>,
}

pub struct RuntimeHooks {
Expand Down Expand Up @@ -1825,9 +1828,14 @@ pub struct RuntimeHooks {
transpiler: *mut Transpiler<'static>,
graph: &'static dyn bun_resolver::StandaloneModuleGraph,
),
/// Parse `execArgv` against the `RunCommand` param table (lives in `bun_runtime::cli`, forward-dep).
/// Caller writes `allow_addons` back into `transform_options` and applies `cpu_prof` to the worker VM.
pub parse_worker_exec_argv: unsafe fn(exec_argv: &[bun_core::WTFStringImpl]) -> WorkerExecArgv,
/// Parse a worker's `execArgv` (`bun_runtime::cli::worker_exec_argv`,
/// forward-dep); `None` derives an inheriting worker's defaults from the
/// process argv. cpu-prof is excluded (the parent VM carries it via
/// `parent_cpu_profiler_config`); preloads are excluded too, so an
/// inheriting worker does not re-run CLI `-r` (only an explicit execArgv
/// does; node re-runs in both — widening is a behavior decision).
pub parse_worker_exec_argv:
unsafe fn(exec_argv: Option<&[bun_core::WTFStringImpl]>) -> WorkerExecArgv,
/// `CronJob.clearAllForVM(vm, .teardown)`. `CronJob` lives in
/// `bun_runtime::api::cron`.
pub cron_clear_all_teardown: fn(vm: &mut VirtualMachine),
Expand Down
1 change: 1 addition & 0 deletions src/jsc/bindings/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@ const errors: ErrorCodeMapping = [
// llhttp reports a missing CRLF after a chunk's data as HPE_STRICT,
// distinct from a malformed chunk-size line (HPE_INVALID_CHUNK_SIZE).
["HPE_STRICT", Error],
["ERR_WORKER_INVALID_EXEC_ARGV", Error],
["ERR_NOT_BUILDING_SNAPSHOT", Error],
["ERR_CANNOT_WATCH_SIGINT", Error],
["ERR_INSPECTOR_NOT_AVAILABLE", Error],
Expand Down
63 changes: 42 additions & 21 deletions src/jsc/bindings/JSEnvironmentVariableMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ bool JSEnvironmentVariableMap::put(JSCell* cell, JSGlobalObject* globalObject, P

auto* uid = propertyName.uid();
if (uid && uid->isSymbol()) {
throwTypeError(globalObject, scope, "Cannot convert a symbol to a string"_s);
throwTypeError(globalObject, scope, "Cannot convert a Symbol value to a string"_s);
return false;
}

Expand Down Expand Up @@ -608,6 +608,10 @@ class JSSharedEnvMap final : public JSC::JSNonFinalObject {
static bool deletePropertyByIndex(JSCell*, JSGlobalObject*, unsigned);
static void getOwnPropertyNames(JSObject*, JSGlobalObject*, JSC::PropertyNameArrayBuilder&, JSC::DontEnumPropertiesMode);
static bool defineOwnProperty(JSObject*, JSGlobalObject*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);
static bool preventExtensions(JSC::JSObject*, JSC::JSGlobalObject*)
{
return false;
}

private:
JSSharedEnvMap(JSC::VM& vm, JSC::Structure* structure)
Expand Down Expand Up @@ -728,7 +732,11 @@ bool JSSharedEnvMap::put(JSCell* cell, JSGlobalObject* globalObject, PropertyNam
auto scope = DECLARE_THROW_SCOPE(vm);

auto* uid = propertyName.uid();
if (propertyName.isSymbol() || !uid) {
if (propertyName.isSymbol()) {
throwTypeError(globalObject, scope, "Cannot convert a Symbol value to a string"_s);
return false;
}
Comment thread
robobun marked this conversation as resolved.
if (!uid) {
RELEASE_AND_RETURN(scope, Base::put(cell, globalObject, propertyName, value, slot));
}

Expand Down Expand Up @@ -807,23 +815,22 @@ bool JSSharedEnvMap::defineOwnProperty(JSObject* object, JSGlobalObject* globalO
return false;
}

if (propertyName.isSymbol() || !uid || !descriptor.isDataDescriptor() || !descriptor.value()) {
// getOwnPropertySlot reads the store first; move the entry onto Base so a partial
// descriptor keeps enumerability. Node's EnvDefiner also rejects partials (the regular
// map does); tightening SHARE_ENV is a separate behavior change with its own tests.
if (!propertyName.isSymbol() && uid) {
if (auto* store = sharedEnvStoreFor(object)) {
String existing = store->get(String(uid));
if (!existing.isNull()) {
syncWindowsEnv(store, String(uid), nullptr);
store->remove(String(uid));
object->putDirect(vm, propertyName, jsString(vm, existing), 0);
}
}
}
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, shouldThrow));
if (!descriptor.value()
|| !descriptor.writablePresent() || !descriptor.writable()
|| !descriptor.enumerablePresent() || !descriptor.enumerable()
|| !descriptor.configurablePresent() || !descriptor.configurable()) {
throwError(globalObject, scope, ErrorCode::ERR_INVALID_OBJECT_DEFINE_PROPERTY, "'process.env' only accepts a configurable, writable, and enumerable data descriptor"_s);
return false;
}

if (propertyName.isSymbol()) {
throwTypeError(globalObject, scope, "Cannot convert a Symbol value to a string"_s);
return false;
}

if (!uid) [[unlikely]]
RELEASE_AND_RETURN(scope, Base::defineOwnProperty(object, globalObject, propertyName, descriptor, shouldThrow));

maybeEmitEnvNonstringDeprecation(globalObject, scope, descriptor.value());
RETURN_IF_EXCEPTION(scope, false);
String stringValue = descriptor.value().toWTFString(globalObject);
Expand Down Expand Up @@ -1104,7 +1111,23 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
}

#if OS(WINDOWS)
auto editWindowsEnvVar = JSC::JSFunction::create(vm, globalObject, 0, String("editWindowsEnvVar"_s), jsEditWindowsEnvVar, ImplementationVisibility::Public);
RELEASE_AND_RETURN(scope, wrapInWindowsEnvProxy(globalObject, object, keyArray, /* syncOSEnv */ true));
#else
return object;
#endif
}

#if OS(WINDOWS)
JSC_DEFINE_HOST_FUNCTION(jsNoopEditWindowsEnvVar, (JSGlobalObject*, JSC::CallFrame*))
{
return JSValue::encode(jsUndefined());
}

JSValue wrapInWindowsEnvProxy(Zig::GlobalObject* globalObject, JSC::JSObject* object, JSC::JSArray* keyArray, bool syncOSEnv)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
auto editWindowsEnvVar = JSC::JSFunction::create(vm, globalObject, 0, String("editWindowsEnvVar"_s), syncOSEnv ? jsEditWindowsEnvVar : jsNoopEditWindowsEnvVar, ImplementationVisibility::Public);

JSC::JSFunction* getSourceEvent = JSC::JSFunction::create(vm, globalObject, processObjectInternalsWindowsEnvCodeGenerator(vm), globalObject);
RETURN_IF_EXCEPTION(scope, {});
Expand All @@ -1126,8 +1149,6 @@ JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject)
}

RELEASE_AND_RETURN(scope, result);
#else
return object;
#endif
}
#endif
}
8 changes: 8 additions & 0 deletions src/jsc/bindings/JSEnvironmentVariableMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class JSEnvironmentVariableMap final : public JSC::JSNonFinalObject {
static bool putByIndex(JSC::JSCell*, JSC::JSGlobalObject*, unsigned, JSC::JSValue, bool shouldThrow);
static bool defineOwnProperty(JSC::JSObject*, JSC::JSGlobalObject*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);
static bool deleteProperty(JSC::JSCell*, JSC::JSGlobalObject*, JSC::PropertyName, JSC::DeletePropertySlot&);
static bool preventExtensions(JSC::JSObject*, JSC::JSGlobalObject*)
{
return false;
}

private:
JSEnvironmentVariableMap(JSC::VM& vm, JSC::Structure* structure)
Expand All @@ -55,6 +59,10 @@ class JSEnvironmentVariableMap final : public JSC::JSNonFinalObject {

JSC::JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject);

#if OS(WINDOWS)
JSC::JSValue wrapInWindowsEnvProxy(Zig::GlobalObject* globalObject, JSC::JSObject* object, JSC::JSArray* keyArray, bool syncOSEnv);
#endif

// Setting TZ must make *existing* Date instances recompute local time. JSC's DateCache
// reset only clears shared slots; live DateInstances keep a Ref to DateInstanceData
// whose gregorian cache still matches, so walk the heap and invalidate those.
Expand Down
21 changes: 20 additions & 1 deletion src/jsc/bindings/ZigGlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -593,13 +593,27 @@ extern "C" JSC::JSGlobalObject* Zig__GlobalObject__create(void* console_client,

#if OS(WINDOWS)
JSC::JSObject* env = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), size >= JSFinalObject::maxInlineCapacity ? JSFinalObject::maxInlineCapacity : size);
JSC::JSArray* keyArray = JSC::constructEmptyArray(globalObject, nullptr, size);
scope.assertNoException();
unsigned keyIndex = 0;
size_t i = 0;
for (auto k : map) {
keyArray->putByIndexInline(globalObject, keyIndex++, jsString(vm, k.key), false);
scope.assertNoException();
// Numeric env keys hit putDirectIndex → defineOwnProperty (declares a
// ThrowScope). Seeded values are JSStrings so only OOM can throw.
env->putDirectMayBeIndex(globalObject, JSC::Identifier::fromString(vm, k.key.convertToASCIIUppercase()), strings.at(i++));
scope.assertNoException();
}
JSValue wrapped = Bun::wrapInWindowsEnvProxy(globalObject, env, keyArray, /* syncOSEnv */ false);
scope.assertNoException();
globalObject->m_processEnvObject.set(vm, globalObject, wrapped.getObject());
#else
// Same exotic object as the main thread so writes inside the
// worker coerce to string, reject symbol keys, and validate
// defineProperty like Node's EnvSetter/EnvDefiner.
auto* envStructure = Bun::JSEnvironmentVariableMap::createStructure(vm, globalObject, globalObject->objectPrototype());
JSC::JSObject* env = Bun::JSEnvironmentVariableMap::create(vm, envStructure);
#endif
size_t i = 0;
for (auto k : map) {
// Numeric env keys hit putDirectIndex → defineOwnProperty (declares a
Expand All @@ -608,6 +622,7 @@ extern "C" JSC::JSGlobalObject* Zig__GlobalObject__create(void* console_client,
scope.assertNoException();
}
globalObject->m_processEnvObject.set(vm, globalObject, env);
#endif
} else if (options.sharedEnvStore) {
// worker_threads SHARE_ENV: join the env tree the spawning thread
// resolved. Consumed like options.env, and published on the context
Expand Down Expand Up @@ -3267,6 +3282,10 @@ JSC_DEFINE_HOST_FUNCTION(functionJsGc,
extern "C" [[ZIG_EXPORT(nothrow)]] void JSC__JSGlobalObject__addGc(JSC::JSGlobalObject* globalObject)
{
auto& vm = JSC::getVM(globalObject);
// Also reached from web_worker.rs start_vm before the worker thread takes
// the API lock; putDirectNativeFunction allocates and asserts the lock.
// JSLock is recursive, so this is a no-op on the main path.
JSC::JSLockHolder locker(vm);
globalObject->putDirectNativeFunction(vm, globalObject, JSC::Identifier::fromString(vm, "gc"_s), 0, functionJsGc, ImplementationVisibility::Public, JSC::NoIntrinsic, PropertyAttribute::DontEnum | 0);
}

Expand Down
24 changes: 24 additions & 0 deletions src/jsc/bindings/webcore/JSWorker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@
#include "JSEnvironmentVariableMap.h"
#include <JavaScriptCore/JSMap.h>

extern "C" bool Bun__Worker__validateExecArgv(WTF::StringImpl* const* argv, size_t len, BunString* outMessage);
extern "C" bool Bun__Worker__validateWorkerNodeOptions(WTF::StringImpl* nodeOptions, BunString* outMessage);

namespace WebCore {
using namespace JSC;

Expand Down Expand Up @@ -294,6 +297,20 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWorkerDOMConstructor::
env.add(key.impl()->isolatedCopy(), str);
}

// Only an explicit env's NODE_OPTIONS is validated (Rust skips when
// byte-identical to the parent's): https://github.com/nodejs/node/blob/main/src/node_worker.cc
if (envValue && envValue.isCell()) {
auto nodeOptions = env.find("NODE_OPTIONS"_s);
if (nodeOptions != env.end()) {
BunString invalidNodeOptions = BunStringEmpty;
if (!Bun__Worker__validateWorkerNodeOptions(nodeOptions->value.impl(), &invalidNodeOptions)) {
auto message = makeString("Initiated Worker with invalid NODE_OPTIONS env variable: "_s, invalidNodeOptions.transferToWTFString());
throwScope.throwException(lexicalGlobalObject, Bun::createError(globalObject, Bun::ErrorCode::ERR_WORKER_INVALID_EXEC_ARGV, message));
return {};
}
}
}

options.env.emplace(WTF::move(env));
}
}
Expand Down Expand Up @@ -333,6 +350,13 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSWorkerDOMConstructor::
execArgv.append(str);
});
RETURN_IF_EXCEPTION(throwScope, {});
BunString invalidExecArgv = BunStringEmpty;
static_assert(sizeof(WTF::String) == sizeof(WTF::StringImpl*));
if (!Bun__Worker__validateExecArgv(reinterpret_cast<WTF::StringImpl* const*>(execArgv.begin()), execArgv.size(), &invalidExecArgv)) {
auto message = makeString("Initiated Worker with invalid execArgv flags: "_s, invalidExecArgv.transferToWTFString());
throwScope.throwException(lexicalGlobalObject, Bun::createError(globalObject, Bun::ErrorCode::ERR_WORKER_INVALID_EXEC_ARGV, message));
return {};
}
options.execArgv.emplace(WTF::move(execArgv));
}
}
Expand Down
16 changes: 9 additions & 7 deletions src/jsc/bindings/webcore/Worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -569,7 +569,9 @@ bool Worker::dispatchErrorWithValue(Zig::GlobalObject* workerGlobalObject, JSVal
// property read must not propagate exceptions out of this function.
auto& vm = JSC::getVM(workerGlobalObject);
auto scope = DECLARE_TOP_EXCEPTION_SCOPE(vm);
// A TerminationException survives CLEAR_IF_EXCEPTION; do not enter JS with one pending.
// A TerminationException can be pending here (CLEAR_IF_EXCEPTION at the
// call site cannot clear it); SerializedScriptValue::create enters JS and
// asserts on entry with one on the VM.
if (scope.exception())
return false;

Expand Down Expand Up @@ -776,12 +778,12 @@ extern "C" void WebWorker__dispatchError(Zig::GlobalObject* globalObject, Worker
JSValue error = JSC::JSValue::decode(errorValue);
WTF::String messageStr = message->transferToWTFString();
auto& vm = JSC::getVM(globalObject);
// terminate() mid-entry-evaluation routes the rejected entry promise here;
// do not run JS (dispatchEvent, structured clone) with it still pending.
if (vm.hasPendingTerminationException()) {
worker->dispatchErrorWithMessage(WTF::move(messageStr), {});
return;
}
// terminate() may land mid-entry now that 'online' fires first; the pending
// TerminationException is non-clearable and the dispatch/serialize below enter
// JS (executeCallImpl asserts !exception()), so post parent-side only.
if (vm.hasPendingTerminationException()) [[unlikely]]
return worker->dispatchErrorWithMessage(WTF::move(messageStr), {});

ErrorEvent::Init init;
init.message = messageStr.isolatedCopy();
init.error = error;
Expand Down
Loading