wasmerio/wasmer · critical

Internal error: more than one synchronized link operation ac

Error message

Internal error: more than one synchronized link operation active

What it means

`synchronize_link_operation` broadcasts a rendezvous barrier to all instance groups via `send_pending_operation_barrier.try_broadcast`. The barrier bus is intentionally depth-one (single-flight): only one synchronized link operation may be in flight at a time. If `try_broadcast` fails, a previous operation's barrier message was never consumed (or a second operation is concurrently active), which is an unrecoverable internal invariant breach, so the library panics.

Source

Thrown at lib/wasix/src/state/linker/sync/linker_shared.rs:229

    ) {
        trace!(?op, "Synchronizing link operation");

        let num_groups = linker_state_write_lock.send_pending_operation.rx_count();

        if num_groups <= 1 {
            trace!("No other living instance groups, nothing to do");
            drop(topology);
            return;
        }

        let barrier = Arc::new(Barrier::new(num_groups));
        // Single-flight barrier envelope (bus depth is one intentionally).
        if linker_state_write_lock
            .send_pending_operation_barrier
            .try_broadcast(barrier.clone())
            .is_err()
        {
            panic!("Internal error: more than one synchronized link operation active")
        }

        // Wake followers so syscall paths re-enter cooperative DL helpers promptly.
        self.dl_operation_pending.store(true, Ordering::SeqCst);

        trace!("Signalling wasix threads to wake up");
        for thread in wasi_process
            .all_threads()
            .into_iter()
            .filter(|tid| *tid != self_thread_id)
        {
            wasi_process.signal_thread(&thread, wasmer_wasix_types::wasi::Signal::Sigwakeup);
        }

        trace!(%num_groups, "Waiting at barrier");
        barrier.wait();

        trace!("All threads now processing dl op");

View on GitHub (pinned to 8c4b9ee9d3)

Solutions

  1. Verify every `synchronize_link_operation` caller holds the TopologyToken acquired via `acquire_topology_token` so calls are serialized
  2. Check for hung/panicking follower threads that never reached the barrier and left stale messages on the barrier bus
  3. Restart the WASIX process — the single-flight bus cannot be reconciled after this panic
  4. If you maintain wasix, ensure followers always drain `recv_pending_operation_barrier` even on error paths
Defensive patterns

Strategy: retry

Validate before calling

// Only start a link op while holding the topology token and no op is pending
if !linker.shared.dl_operation_pending_load() {
    // safe window to enter synchronize_link_operation
}

Type guard

fn link_operation_idle(pending: &AtomicBool) -> bool {
    !pending.load(Ordering::SeqCst)
}

Try / catch

let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    linker.shared.synchronize_link_operation(token, op, guard, &mut group, &proc, &tid);
}));
if result.is_err() { /* a stale epoch is in flight: restart the process tree */ }

Prevention

When it happens

Trigger: Starting a new `synchronize_link_operation` while a previous barrier epoch is still unconsumed on the barrier bus (e.g. a follower crashed or hung between the two barrier waits); two threads entering `synchronize_link_operation` concurrently due to a missing TopologyToken; retrying the call after a partial failure.

Common situations: Hung or cancelled WASIX threads that left the previous DL operation half-finished; topology-token misuse in custom linker code; bug fixes or partial upgrades of wasix internals that allow overlapping link operations.

Related errors


AI-assisted analysis of wasmerio/wasmer@8c4b9ee9d3 (2026-09-01). Data as JSON: /api/errors/acc8f5f5a19b230c. Report an issue: GitHub.