swc-project/swc · critical

Should able to write into memory view

Error message

Should able to write into memory view

What it means

After the guest allocator returns a pointer, the host writes the serialized program bytes into the plugin's wasm memory via view.write_buf(ptr_start, bytes). The expect panics when that write crosses the guest memory bounds - the allocator returned a bad pointer, the guest could not grow its memory (wasm max / store limits), or the pointer was computed against a different memory snapshot.

Source

Thrown at crates/swc_plugin_runner/src/memory_interop.rs:38

#[cfg_attr(debug_assertions, tracing::instrument(level = "info", skip_all))]
pub fn write_into_memory_view<F>(
    view: &mut dyn runtime::Caller<'_>,
    serialized_bytes: &PluginSerializedBytes,
    get_allocated_ptr: F,
) -> (u32, u32)
where
    F: Fn(&mut dyn runtime::Caller<'_>, usize) -> u32,
{
    let serialized_len = serialized_bytes.as_slice().len();

    let ptr_start = get_allocated_ptr(view, serialized_len);
    let serialized_size = serialized_len
        .try_into()
        .expect("Should be able to convert to i32");

    // Note: it's important to get a view from memory _after_ alloc completes
    view.write_buf(ptr_start, serialized_bytes.as_slice())
        .expect("Should able to write into memory view");

    (ptr_start, serialized_size)
}

/// Set `return` value to pass into guest from functions returning values with
/// non-deterministic size like `Vec<Comment>`. Guest pre-allocates a struct to
/// contain ptr to the value, host in here allocates guest memory for the actual
/// value then returns its ptr with length to the preallocated struct.
#[cfg_attr(debug_assertions, tracing::instrument(level = "info", skip_all))]
pub fn allocate_return_values_into_guest(
    caller: &mut dyn runtime::Caller<'_>,
    allocated_ret_ptr: u32,
    serialized_bytes: &PluginSerializedBytes,
) {
    let serialized_bytes_len: usize = serialized_bytes.as_slice().len();
    let serialized_bytes_len = serialized_bytes_len
        .try_into()
        .expect("Should be able to convert size");

View on GitHub (pinned to 5176682b65)

Solutions

  1. Align the plugin's swc_core version with the host swc version and rebuild with wasm32-wasi --release.
  2. Reduce the payload: transform per module, keep files a reasonable size, avoid plugins on giant generated files.
  3. Capture the panic boundary with catch_unwind/subprocess isolation so the build reports the file instead of aborting.
  4. Check the plugin does not override or break the allocator (no custom global allocator in the guest).
Defensive patterns

Strategy: try-catch

Try / catch

// Wrap the whole plugin transform (alloc + write into guest memory) so an
// out-of-bounds write is a per-file error, not a process abort.
let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    compiler.process_js_file(&fm, &handler, &config_with_plugins)
}));
let out = match out {
    Ok(r) => r,
    Err(_) => {
        tracing::error!(file = %fm.name, "plugin guest memory write failed; retrying without plugins");
        compiler.process_js_file(&fm, &handler, &config_without_plugins)
    }
};

Prevention

When it happens

Trigger: write_into_memory_view during PluginTransformExecutor::run: guest alloc 'succeeds' but ptr+len exceeds current memory size; guest memory.grow failed so the region is not mapped; plugin's allocator is corrupted or from an ABI-mismatched build.

Common situations: Very large files plus plugins that force big serialized AST copies; plugin built with mismatched swc_core (allocator protocol differs); wasm store memory limits configured in the embedding runtime.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/3e59bde5669dd6ef. Report an issue: GitHub.