swc-project/swc · critical

Should be serializable

Error message

Should be serializable

What it means

Host import that serves a plugin its transform config: it serializes `transform_plugin_config` to a JSON string and then encodes that string as versioned CBOR for the guest; a CBOR encode failure panics. A JSON-unserializable config is already handled gracefully (serde_json error maps to `.ok()` and the host returns 0), so the panic is confined to encoding a plain String - which only fails under host/guest schema divergence or a runner bug.

Source

Thrown at crates/swc_plugin_runner/src/imported_fn/metadata_context.rs:63

    let mut buf = env.mutable_context_key_buffer.lock();
    copy_bytes_into_host(caller, bytes_ptr, bytes_ptr_len, &mut buf);
}

#[cfg_attr(debug_assertions, tracing::instrument(level = "info", skip_all))]
pub fn get_transform_plugin_config(
    caller: &mut dyn runtime::Caller<'_>,
    env: &MetadataContextHostEnvironment,
    allocated_ret_ptr: u32,
) -> i32 {
    let config_value = env.transform_plugin_config.as_ref();
    if let Some(config_value) = config_value {
        // Lazy as possible as we can - only deserialize json value if transform plugin
        // actually needs it.
        let config = serde_json::to_string(config_value).ok();
        if let Some(config) = config {
            let serialized =
                PluginSerializedBytes::try_serialize(&VersionedSerializable::new(config))
                    .expect("Should be serializable");

            allocate_return_values_into_guest(caller, allocated_ret_ptr, &serialized);

            return 1;
        }
    }
    0
}

#[cfg_attr(debug_assertions, tracing::instrument(level = "info", skip_all))]
pub fn get_transform_context(
    caller: &mut dyn runtime::Caller<'_>,
    env: &MetadataContextHostEnvironment,
    key: u32,
    allocated_ret_ptr: u32,
) -> i32 {
    let Some(value) = env
        .metadata_context

View on GitHub (pinned to 5176682b65)

Solutions

  1. Rebuild/pin the plugin against the swc_core version shipped in your @swc/core
  2. Upgrade @swc/core and the plugin together
  3. Verify the plugin config is plain JSON (this avoids the silent return-0 path where the plugin sees no config)
  4. Report upstream if matched versions still crash

Example fix

// before
 jsc: { experimental: { plugins: [['my-plugin', cfg]] } } // plugin wasm built on old swc_core

// after
// rebuild my-plugin.wasm against the swc_core in @swc/core, keep config as plain JSON
 jsc: { experimental: { plugins: [['my-plugin', { strict: true }]] } }
Defensive patterns

Strategy: validation

Validate before calling

// ensure plugin config is plain-JSON serializable and plugin version aligns
const cfg = swcCfg.jsc.experimental.plugins[0][1];
JSON.stringify(cfg); // throws early if non-serializable (host would silently pass null otherwise)
assertPluginHostAligned(pluginName, coreVersion);

Try / catch

// wrap plugin-enabled compilation in a worker; on native crash, retry once with plugins disabled
const out = await runInWorker('swc-transform', input).catch(() =>
  runInWorker('swc-transform', input, { plugins: false })
);

Prevention

When it happens

Trigger: A plugin calls get_transform_config and the CBOR encoding of the config string fails - realistically only when the plugin binary's swc_core schema does not match the host runner, or the runner's encoder is broken.

Common situations: Config passed through @swc/core `jsc.experimental.plugins` with a plugin binary compiled against a different swc_core; partial upgrades; in-house plugins drifting from the app's compiler version.

Related errors


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