swc-project/swc · error · anyhow::Error

failed to deserialize program: {}

Error message

failed to deserialize program: {}

What it means

Emitted by the build_print_sync wasm macro when the first argument `s` cannot be deserialized into a swc Program. print expects an AST object previously produced by parse - the exact serialized Program shape (type: 'Module'|'Script', span, body, ...) - not source code and not a hand-built JSON tree.

Source

Thrown at crates/binding_macros/src/wasm.rs:253

    pub fn print_sync(s: $crate::wasm::JsValue, opts: $crate::wasm::JsValue) -> Result<$crate::wasm::JsValue, $crate::wasm::JsValue> {
      use $crate::wasm::PrintArgs;

      let c = $crate::wasm::compiler();

      $crate::wasm::try_with_handler_globals(
          c.cm.clone(),
          $opt,
          |_handler| {
              c.run(|| {
                  let opts: $crate::wasm::Options = if opts.is_null() || opts.is_undefined() {
                      Default::default()
                  } else {
                    $crate::wasm::serde_wasm_bindgen::from_value(opts)
                      .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to parse options: {}", e))?
                  };

                  let program: $crate::wasm::Program = $crate::wasm::serde_wasm_bindgen::from_value(s)
                    .map_err(|e| $crate::wasm::anyhow::anyhow!("failed to deserialize program: {}", e))?;
                  let s = $crate::wasm::anyhow::Context::context(c
                    .print(
                        &program,
                        PrintArgs {
                          inline_sources_content: true,
                          source_map: opts.source_maps
                              .clone()
                              .unwrap_or($crate::wasm::SourceMapsConfig::Bool(false)),
                          emit_source_map_columns: opts.config.emit_source_map_columns.into_bool(),
                          codegen_config: swc_core::ecma::codegen::Config::default()
                              .with_target(opts.codegen_target().unwrap_or($crate::wasm::EsVersion::Es2020))
                              .with_minify(opts.config.minify.into()),
                          ..Default::default()
                        },
                    ),"failed to print code")?;

                    serde_wasm_bindgen::to_value(&s)
                    .map_err(|e| anyhow::anyhow!("failed to serialize json: {}", e))

View on GitHub (pinned to d7d7434666)

Solutions

  1. Feed print exactly the value returned by parse/parseSync of the same binding version
  2. If you must persist ASTs, re-parse the source instead of printing stale AST JSON after upgrading swc
  3. Do not prune required fields (span, ctxt) when post-processing the AST JSON; check the serde error detail for the missing field name
  4. Use transform() if your input is source text, not an AST

Example fix

// before: print expects an AST, not source text
printSync('const a = 1;', {});

// after: parse first, then print
const { program } = parseSync('const a = 1;', null);
printSync(program, {});
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure the value is an AST, not source text, before printing
function assertProgramLike(v) {
  if (typeof v === 'string')
    throw new TypeError('print expects the AST from parseSync, not source code - use transform() for text input');
  if (typeof v !== 'object' || v === null || !('type' in v))
    throw new TypeError('print expects a Program object with a type field');
}

Type guard

function isProgram(v) {
  return typeof v === 'object' && v !== null &&
    (v.type === 'Module' || v.type === 'Script') &&
    'span' in v && Array.isArray(v.body);
}

Try / catch

try {
  const out = printSync(program, printOpts);
} catch (e) {
  if (String(e).startsWith('failed to deserialize program')) {
    // re-parse from source instead of repairing a stale/edited AST
    const { program: fresh } = parseSync(src, parseOpts);
    return printSync(fresh, printOpts);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing raw source code (a JsString/string) to printSync instead of the AST from parseSync; passing an AST serialized by a different swc version whose serialized Program shape drifted; editing/lossy-transforming the AST JSON (dropping required fields like span) before printing; passing arbitrary JSON that happens to have a body array.

Common situations: Misreading the API (print = AST-to-code, transform = code-to-code); round-tripping ASTs through storage and a different swc version on read; trimming AST JSON for size and deleting required fields.

Related errors


AI-assisted analysis of swc-project/swc@d7d7434666 (2026-08-16). Data as JSON: /api/errors/20cbabd5f7e63cf4. Report an issue: GitHub.