swc-project/swc · error

unable to access unknown nodes

Error message

unable to access unknown nodes

What it means

PatternExt::get_type_ann (crates/swc_typescript/src/fast_dts/util/ast_ext.rs) is the shared helper fast_dts uses to read a type annotation off any binding pattern (unwrapping Pat::Assign to its left side first). Under `--cfg swc_ast_unknown`, a pattern decoded as `Unknown(tag, value)` falls into the catch-all and panics, because there is no annotation field to read on an opaque node. It fires during many passes (parameter checks, declaration transforms), always on deserialized cross-version AST.

Source

Thrown at crates/swc_typescript/src/fast_dts/util/ast_ext.rs:87

    fn set_type_ann(&mut self, type_anno: Option<Box<TsTypeAnn>>);
    fn bound_names<F: FnMut(&BindingIdent)>(&self, f: &mut F);
}

impl PatExt for Pat {
    fn get_type_ann(&self) -> &Option<Box<TsTypeAnn>> {
        let pat = match self {
            Pat::Assign(assign_pat) => &assign_pat.left,
            _ => self,
        };

        match pat {
            Pat::Ident(binding_ident) => &binding_ident.type_ann,
            Pat::Array(array_pat) => &array_pat.type_ann,
            Pat::Rest(rest_pat) => &rest_pat.type_ann,
            Pat::Object(object_pat) => &object_pat.type_ann,
            Pat::Assign(_) | Pat::Invalid(_) | Pat::Expr(_) => &None,
            #[cfg(swc_ast_unknown)]
            _ => panic!("unable to access unknown nodes"),
        }
    }

    fn set_type_ann(&mut self, type_anno: Option<Box<TsTypeAnn>>) {
        let pat = match self {
            Pat::Assign(assign_pat) => &mut assign_pat.left,
            _ => self,
        };

        match pat {
            Pat::Ident(binding_ident) => binding_ident.type_ann = type_anno,
            Pat::Array(array_pat) => array_pat.type_ann = type_anno,
            Pat::Rest(rest_pat) => rest_pat.type_ann = type_anno,
            Pat::Object(object_pat) => object_pat.type_ann = type_anno,
            Pat::Assign(_) | Pat::Invalid(_) | Pat::Expr(_) => {}
            #[cfg(swc_ast_unknown)]
            _ => panic!("unable to access unknown nodes"),
        }

View on GitHub (pinned to 5176682b65)

Solutions

  1. Unify on one swc_core version across producer and consumer and rebuild.
  2. Upgrade swc_typescript/swc_core past the version that introduced the new pattern node.
  3. Re-parse from source with the same build before FastDts::transform.
  4. Keep --cfg swc_ast_unknown only in plugin/ABI-stability targets, and rebuild those whenever the producing side changes.

Example fix

// before
let ty = pat.get_type_ann(); // panics on Pat::Unknown

// after: reject foreign payloads before the transform
assert_payload_version_matches(&bytes)?;
let ty = pat.get_type_ann();
Defensive patterns

Strategy: type-guard

Validate before calling

// Before any code path that calls get_type_ann on deserialized patterns:
#[cfg(swc_ast_unknown)]
fn safe_get_type_ann(p: &Pat) -> Option<&Option<Box<TsTypeAnn>>> {
    if pat_is_known(p) { Some(p.get_type_ann_ref()) } else { None }
}

Type guard

#[cfg(swc_ast_unknown)]
fn pat_is_known(p: &Pat) -> bool {
    !matches!(p, Pat::Unknown(..))
}

Try / catch

use std::panic::{catch_unwind, AssertUnwindSafe};
let issues = catch_unwind(AssertUnwindSafe(|| dts.transform(&mut program)))
    .unwrap_or_else(|p| { tracing::error!("fast_dts rejected AST: {:?}", p.downcast_ref::<&str>()); Vec::new() });

Prevention

When it happens

Trigger: Any FastDts pass reads a type annotation from a pattern node whose tag is unknown to this swc_ecma_ast build — e.g. a new binding-pattern variant from a newer swc release inside a deserialized Program.

Common situations: Plugin/host swc version skew; two swc_ecma_ast versions resolved in one binary; persisted AST inputs replayed after upgrading @swc/core or swc_cli.

Related errors


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