swc-project/swc · error

unable to access unknown nodes

Error message

unable to access unknown nodes

What it means

fast_dts in swc_typescript (a port of oxc's fast-dts .d.ts emitter) matches exhaustively on SWC AST enums. When compiled with the swc_ast_unknown cfg — set for wasm plugin builds via `--cfg=swc_ast_unknown` in the .cargo/config.toml generated by `swc plugin new` — those enums gain an Unknown(tag, payload) variant carrying node kinds this swc_core version does not know. This arm matches TsParamPropParam (Ident/Assign) while computing is_required for constructor parameter properties and panics because the unknown node cannot be inspected.

Source

Thrown at crates/swc_typescript/src/fast_dts/class.rs:299

            );
        }
    }

    pub(crate) fn transform_constructor_params(
        &mut self,
        params: &mut [ParamOrTsParamProp],
        private_constructor: bool,
    ) -> Vec<ClassMember> {
        let mut is_required = false;
        let mut private_properties = Vec::new();
        for param in params.iter_mut().rev() {
            match param {
                ParamOrTsParamProp::TsParamProp(ts_param_prop) => {
                    is_required |= match &ts_param_prop.param {
                        TsParamPropParam::Ident(binding_ident) => !binding_ident.optional,
                        TsParamPropParam::Assign(_) => false,
                        #[cfg(swc_ast_unknown)]
                        _ => panic!("unable to access unknown nodes"),
                    };
                    if let Some(private_prop) =
                        self.transform_constructor_ts_param(ts_param_prop, is_required)
                    {
                        private_properties.push(private_prop);
                    }
                    ts_param_prop.readonly = false;
                    ts_param_prop.accessibility = None;
                }
                ParamOrTsParamProp::Param(param) => {
                    if private_constructor {
                        continue;
                    }

                    self.transform_fn_param(param, is_required);
                    is_required |= match &param.pat {
                        Pat::Ident(binding_ident) => !binding_ident.optional,
                        Pat::Array(array_pat) => !array_pat.optional,

View on GitHub (pinned to 5176682b65)

Solutions

  1. Rebuild the plugin with the swc_core version that matches the host @swc/core (check @swc/core's bundled swc_core)
  2. Bump swc_core in the plugin's Cargo.toml to the latest release so the new node kinds decode as concrete variants
  3. Pre-scan the Program for Unknown nodes and skip fast_dts (or skip the file) when any are found
  4. Pin the host SWC to a version whose AST the plugin understands until the plugin is rebuilt

Example fix

# before (plugin Cargo.toml)
swc_core = { version = "=16.x", features = ["ecma_plugin_transform"] }
# host @swc/core ships newer swc_ecma_ast -> TsParamPropParam::Unknown -> panic

# after
swc_core = { version = "=latest-matching-host", features = ["ecma_plugin_transform"] }
# node kind now decodes to Ident/Assign; match succeeds
Defensive patterns

Strategy: type-guard

Validate before calling

#[cfg(swc_ast_unknown)]
fn ctor_params_contain_unknown(c: &swc_ecma_ast::Class) -> bool {
    use swc_ecma_ast::*;
    c.body.iter().any(|m| matches!(m, ClassMember::Constructor(ctor) if
        ctor.params.iter().any(|p| matches!(p,
            ParamOrTsParamProp::Unknown(..)
                | ParamOrTsParamProp::TsParamProp(tp) if matches!(tp.param,
                    TsParamPropParam::Unknown(..))))))
}
// skip fast_dts for classes failing this check

Type guard

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

Try / catch

let out = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
    run_fast_dts(&mut program) // your fast_dts invocation
}));
if out.is_err() {
    // unknown AST node: emit a diagnostic and skip .d.ts for this file
    handler.struct_err("fast_dts skipped: AST contains unknown nodes (plugin/host version mismatch)").emit();
}

Prevention

When it happens

Trigger: Running fast_dts over a Program whose constructor has a parameter property (e.g. `constructor(private x)` or `constructor(private x = 1)`) where the TsParamPropParam was deserialized as Unknown — typically a plugin built with --cfg=swc_ast_unknown receiving an AST from a newer host @swc/core than its pinned swc_core.

Common situations: Version skew between the running @swc/core host and the plugin's swc_core dependency; rebuilding a plugin against an older swc_core; new AST variants added to swc_ecma_ast in newer SWC releases reaching older plugins.

Related errors


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