wasmerio/wasmer · critical
wasix linker bootstrap invariant violated: expected exactly
Error message
wasix linker bootstrap invariant violated: expected exactly one DL bus subscriber on each sender (pending_operation rx={op_rx}, barrier rx={barrier_rx}); `LinkerShared::bootstrap_exclusive_write_then` must only run during main `Linker::new` finalization before additional instance groups attach receivers What it means
This is an internal bootstrap invariant check in the WASIX dynamic-linking (DL) machinery. `bootstrap_exclusive_write_then` is only safe to run while exactly one instance group (the main group created by `Linker::new`) is subscribed to each DL broadcast bus (`send_pending_operation` and `send_pending_operation_barrier`). If either sender reports more than one receiver, it means a second instance group attached receivers before bootstrap exclusive writes finished, so the exclusive-write contract is broken and the library panics.
Source
Thrown at lib/wasix/src/state/linker/sync/linker_shared.rs:63
impl LinkerShared {
/// Wraps freshly constructed [`LinkerState`] for the owning process/module tree (initially only
/// the main [`super::super::Linker::new`] path).
pub(in crate::state::linker) fn new(linker_state: LinkerState) -> Self {
Self {
linker_state: Arc::new(RwLock::new(linker_state)),
topology_coordinator: TopologyCoordinator::new(),
dl_operation_pending: Arc::new(AtomicBool::new(false)),
}
}
/// Panics unless both DL buses have exactly one receiver — validates main-group bootstrap before
/// exclusive writes (see [`Self::bootstrap_exclusive_write_then`]).
fn assert_exactly_one_dl_bus_subscriber(ls: &LinkerState) {
let op_rx = ls.send_pending_operation.rx_count();
let barrier_rx = ls.send_pending_operation_barrier.rx_count();
if op_rx != 1 || barrier_rx != 1 {
panic!(
"wasix linker bootstrap invariant violated: expected exactly one DL bus subscriber \
on each sender (pending_operation rx={op_rx}, barrier rx={barrier_rx}); \
`LinkerShared::bootstrap_exclusive_write_then` must only run during main \
`Linker::new` finalization before additional instance groups attach receivers"
);
}
}
/// Exclusive [`LinkerState`] write for main linker bootstrap only.
///
/// # Safety
///
/// Must run only while exactly one instance group has subscribed to both DL buses (verified
/// after the lock is taken — mismatch panics in release builds). Caller must respect instance-group /
/// linker lock ordering used in [`super::super::Linker::new`].
pub(in crate::state::linker) unsafe fn bootstrap_exclusive_write_then<R>(
&self,
f: impl FnOnce(&mut LinkerState) -> R,View on GitHub (pinned to 8c4b9ee9d3)
Solutions
- Ensure all `bootstrap_exclusive_write_then` calls happen only inside the main `Linker::new` finalization path, before any instance group is created
- Move instance-group creation (spawn/prepare_for_instance_group) after linker bootstrap completes, behind the topology coordinator so it cannot race bootstrap writes
- Inspect the panic's rx counts to see which bus has extra receivers and find which code path attaches receivers early
- If you patched wasix internals, audit receiver attach points to confirm they only run after bootstrap
Example fix
// before: attach instance group then bootstrap write
let group = linker.prepare_for_instance_group(...);
linker.shared.bootstrap_exclusive_write_then(|ls| { /* ... */ });
// after: bootstrap first, then attach groups
linker.shared.bootstrap_exclusive_write_then(|ls| { /* ... */ });
let group = linker.prepare_for_instance_group(...); Defensive patterns
Strategy: validation
Validate before calling
// Before any bootstrap_exclusive_write_then, verify bus subscription counts let ls = linker.shared.try_read_linker_state()?; assert_eq!(ls.send_pending_operation.rx_count(), 1); assert_eq!(ls.send_pending_operation_barrier.rx_count(), 1);
Type guard
fn is_main_group_bootstrap_safe(ls: &LinkerState) -> bool {
ls.send_pending_operation.rx_count() == 1
&& ls.send_pending_operation_barrier.rx_count() == 1
} Try / catch
// This is a panic, not a recoverable error — use catch_unwind at the embedder boundary
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
linker.shared.bootstrap_exclusive_write_then(|ls| { /* ... */ })
})); Prevention
- Only call bootstrap_exclusive_write_then inside Linker::new finalization
- Create instance groups strictly after linker bootstrap completes
- Serialize group attachment behind the topology coordinator
- Log rx_count values during startup to catch early attachers in CI
When it happens
Trigger: Calling `LinkerShared::bootstrap_exclusive_write_then` after additional instance groups have subscribed to the DL buses (e.g. creating instance groups, spawning follower threads/instances, or calling `prepare_for_instance_group`/`create_instance_group` before all bootstrap writes complete). Also triggered by receiver-count races where a follower group attaches its bus receivers concurrently with `Linker::new` finalization.
Common situations: Embedding code that spawns or attaches extra WASIX instance groups during linker construction; reordering linker initialization so group creation races with `Linker::new`; custom patches or extensions to the linker that perform exclusive writes late in startup.
Related errors
- Internal error: more than one synchronized link operation ac
- Internal error: missing symbol resolution record for '{0}'.{
- Internal error: failed to resolve function {}: {e:?}
- The linker state's lock is poisoned
- Internal error: failed to resolve exported function {}: {e:?
AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01).
Data as JSON: /api/errors/a4b77c55a94bfe4b.
Report an issue: GitHub.