swc-project/swc · error

unable to access unknown nodes

Error message

unable to access unknown nodes

What it means

transform_expr_to_ts_type (crates/swc_typescript/src/fast_dts/types.rs:24) converts a constant expression to a literal TS type (string/bool/null/number/bigint literals) for const initializers, enum members and property defaults. In `--cfg swc_ast_unknown` builds, a literal kind this build does not recognize decodes to `Unknown(tag, value)` and the inner Lit match panics, because no TsLit can be fabricated from an opaque node. Like all such panics, it appears only when deserialized AST from a different swc_ecma_ast version reaches fast_dts.

Source

Thrown at crates/swc_typescript/src/fast_dts/types.rs:37

    FastDts,
};
use crate::fast_dts::util::ast_ext::PropNameExit;

impl FastDts {
    pub(crate) fn transform_expr_to_ts_type(&mut self, expr: &Expr) -> Option<Box<TsType>> {
        match expr {
            Expr::Ident(ident) if ident.sym == "undefined" => {
                Some(ts_keyword_type(TsKeywordTypeKind::TsUndefinedKeyword))
            }
            Expr::Lit(lit) => match lit {
                Lit::Str(string) => Some(ts_lit_type(TsLit::Str(string.clone()))),
                Lit::Bool(b) => Some(ts_lit_type(TsLit::Bool(*b))),
                Lit::Null(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNullKeyword)),
                Lit::Num(number) => Some(ts_lit_type(TsLit::Number(number.clone()))),
                Lit::BigInt(big_int) => Some(ts_lit_type(TsLit::BigInt(big_int.clone()))),
                Lit::Regex(_) | Lit::JSXText(_) => None,
                #[cfg(swc_ast_unknown)]
                _ => panic!("unable to access unknown nodes"),
            },
            Expr::Tpl(tpl) => self
                .tpl_to_string(tpl)
                .map(|string| ts_lit_type(TsLit::Str(string))),
            Expr::Unary(unary) if Self::can_infer_unary_expr(unary) => {
                let mut expr = self.transform_expr_to_ts_type(&unary.arg)?;
                if unary.op == UnaryOp::Minus {
                    match &mut expr.as_mut_ts_lit_type()?.lit {
                        TsLit::Number(number) => {
                            number.value = -number.value;
                            number.raw = None;
                        }
                        TsLit::BigInt(big_int) => {
                            *big_int.value = -*big_int.value.clone();
                            big_int.raw = None;
                        }
                        _ => {}
                    }

View on GitHub (pinned to 5176682b65)

Solutions

  1. Align producer and consumer on one swc_core/swc_ecma_ast version and rebuild everything together.
  2. Upgrade swc_typescript/swc_core to a version that knows the new literal node.
  3. Prefer same-process parsing of source text over accepting serialized ASTs before FastDts::transform.
  4. Version-gate serialized AST inputs and re-parse on mismatch.

Example fix

// before
let mut program: Program = bincode::deserialize(&cached)?;
dts.transform(&mut program);

// after: cache keyed by swc version, re-parse on mismatch
let mut program = match load_cache(cache_key_of(swc_ecma_ast_version())) {
    Some(p) => p,
    None => parse_program(&cm, &src)?, // same-build parser
};
dts.transform(&mut program);
Defensive patterns

Strategy: type-guard

Validate before calling

// Check literals in const/enum initializers before transform:
#[cfg(swc_ast_unknown)]
fn init_lits_known(decls: &[VarDeclarator]) -> bool {
    decls.iter().all(|d| match &d.init {
        Some(e) => !matches!(e.as_ref(), Expr::Lit(l) if matches!(l, Lit::Unknown(..))),
        None => true,
    })
}

Type guard

#[cfg(swc_ast_unknown)]
fn lit_is_known(l: &Lit) -> bool {
    !matches!(l, Lit::Unknown(..))
}

Try / catch

use std::panic::{catch_unwind, AssertUnwindSafe};
match catch_unwind(AssertUnwindSafe(|| dts.transform(&mut program))) {
    Ok(issues) => issues,
    Err(_) => { program = parse_program(&cm, &src)?; dts.transform(&mut program) }
}

Prevention

When it happens

Trigger: FastDts converts an expression (e.g. `export const s = <newLitKind>` or an enum member initializer) whose literal node was deserialized as an unknown Lit variant from a payload written by a newer swc version.

Common situations: Version skew between a swc wasm plugin (built with swc_ast_unknown) and its @swc/core host; AST round-trip fixtures or caches spanning an upgrade; CI runs that intentionally compile with RUSTFLAGS="--cfg swc_ast_unknown".

Related errors


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