{"record":{"id":"f91f7cf894674b2c","repo":"swc-project/swc","slug":"genericfailure-f91f7c","errorCode":"GenericFailure","errorMessage":"{err:?}","messagePattern":"\\{err:\\?\\}","errorType":"exception","errorClass":"napi::Error","httpStatus":null,"severity":"error","filePath":"crates/swc_nodejs_common/src/lib.rs","lineNumber":12,"sourceCode":"#![deny(warnings)]\n\nuse std::any::type_name;\n\nuse anyhow::Context;\nuse napi::Status;\nuse serde::de::DeserializeOwned;\n\npub trait MapErr<T>: Into<Result<T, anyhow::Error>> {\n    fn convert_err(self) -> napi::Result<T> {\n        self.into()\n            .map_err(|err| napi::Error::new(Status::GenericFailure, format!(\"{err:?}\")))\n    }\n}\n\nimpl<T> MapErr<T> for Result<T, anyhow::Error> {}\n\npub fn get_deserialized<T, B>(buffer: B) -> napi::Result<T>\nwhere\n    T: DeserializeOwned,\n    B: AsRef<[u8]>,\n{\n    let mut deserializer = serde_json::Deserializer::from_slice(buffer.as_ref());\n    deserializer.disable_recursion_limit();\n\n    let v = T::deserialize(&mut deserializer)\n        .with_context(|| {\n            format!(\n                \"Failed to deserialize buffer as {}\\nJSON: {}\",\n                type_name::<T>(),","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_nodejs_common/src/lib.rs#L1-L30","documentation":"swc_nodejs_common is the shared plumbing for SWC's Node.js (napi) bindings. Its `MapErr` trait converts any `anyhow::Error` into a `napi::Error` with `Status::GenericFailure`, formatting the entire anyhow error chain with `{:?}` (crates/swc_nodejs_common/src/lib.rs:12). It is the single funnel for failures crossing the Rust→JS boundary — including `get_deserialized`, which serde-deserializes a buffer passed from JavaScript — so on the JS side you only see code 'GenericFailure' plus the debug string.","triggerScenarios":"Calling a Node binding whose Rust body returns Err: `get_deserialized::<T, _>(&buffer)` fails when the JS-passed payload does not deserialize into the expected type (wrong field names/types, malformed JSON), or any internal anyhow error passed through `.convert_err()` in the binding wrapper.","commonSituations":"Passing an options object with a typo'd or renamed field from JS (serde mismatch); version skew between the JS wrapper package and the native module; passing a string where bytes are expected; empty or truncated buffers after a failed fetch/transform.","solutions":["Read the {err:?} text — for serde failures it names the exact field and expected type","Align the JS-side options object with the interface the installed binding version expects","Ensure the native module and the JS package versions match (rebuild/reinstall node_modules)","Log the exact payload right before the call and validate its shape against the binding's schema"],"exampleFix":"// before\nconst res = await transformFn(id, { soruceType: 'module' }); // typo → serde error → GenericFailure\n// after\nconst res = await transformFn(id, { sourceType: 'module' });","handlingStrategy":"try-catch","validationCode":"// JS side: validate the payload shape before calling the native binding\nfunction validateTransformOpts(opts) {\n  const known = new Set(['sourceType', 'filename', 'jsc', 'module', 'isModule', 'minify', 'sourceMaps', 'inlineSourcesContent', 'parser']);\n  for (const key of Object.keys(opts || {})) if (!known.has(key)) throw new Error(`unknown option: ${key}`);\n  if ('sourceType' in (opts || {}) && !['module', 'script', 'unambiguous'].includes(opts.sourceType)) throw new Error('bad sourceType');\n}","typeGuard":"// Narrow a caught error to a napi GenericFailure from the Rust binding\nfunction isNapiGenericFailure(e: unknown): e is Error & { code: 'GenericFailure' } {\n  return e instanceof Error && (e as any).code === 'GenericFailure';\n}","tryCatchPattern":"try {\n  const out = await nativeTransform(id, code, opts);\n} catch (e) {\n  if (isNapiGenericFailure(e)) {\n    // e.message is the Rust-side {err:?} debug string; parse the serde path from it\n    console.error('binding failure:', e.message);\n  }\n  throw e;\n}","preventionTips":["Type the JS options object against the binding's .d.ts instead of hand-rolling payloads","Keep @swc/* JS packages and native binaries in lockstep (dedupe, reinstall after upgrades)","Log the exact arguments on failure so the {err:?} serde path maps to a concrete field","Treat any GenericFailure as an internal binding error — do not retry blindly; fix the payload or versions"],"tags":["napi","nodejs","serde","deserialization","bindings","swc"],"backgroundTag":"native-module-binding-error","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}