{"record":{"id":"653e85b26f55d786","repo":"tauri-apps/tauri","slug":"failed-to-extract-external-binary-filename","errorCode":null,"errorMessage":"failed to extract external binary filename","messagePattern":"failed to extract external binary filename","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tauri-bundler/src/bundle/settings.rs","lineNumber":1208,"sourceCode":"  pub fn external_binaries(&self) -> ResourcePaths<'_> {\n    match self.bundle_settings.external_bin {\n      Some(ref paths) => ResourcePaths::new(paths.as_slice(), true),\n      None => ResourcePaths::new(&[], true),\n    }\n  }\n\n  /// Copies external binaries to a path.\n  ///\n  /// Returns the list of destination paths.\n  pub fn copy_binaries(&self, path: &Path) -> crate::Result<Vec<PathBuf>> {\n    let mut paths = Vec::new();\n\n    for src in self.external_binaries() {\n      let src = src?;\n      let dest = path.join(\n        src\n          .file_name()\n          .expect(\"failed to extract external binary filename\")\n          .to_string_lossy()\n          .replace(&format!(\"-{}\", self.target), \"\"),\n      );\n      fs_utils::copy_file(&src, &dest)?;\n      paths.push(dest);\n    }\n    Ok(paths)\n  }\n\n  /// Copies resources to a path.\n  pub fn copy_resources(&self, path: &Path) -> crate::Result<()> {\n    for resource in self.resource_files().iter() {\n      let resource = resource?;\n      let dest = path.join(resource.target());\n      fs_utils::copy_file(resource.path(), &dest)?;\n    }\n    Ok(())\n  }","sourceCodeStart":1190,"sourceCodeEnd":1226,"githubUrl":"https://github.com/tauri-apps/tauri/blob/52e4b6e71d8632a7e648f866c442e287ecddee34/crates/tauri-bundler/src/bundle/settings.rs#L1190-L1226","documentation":"copy_binaries copies each configured external binary (sidecar, from bundle.externalBin) into the bundle, deriving the destination name from the source's file_name. file_name() is None when the path is a root or ends with '..', so a degenerate externalBin path panics here during bundling.","triggerScenarios":"tauri build with a bundle.externalBin entry in tauri.conf.json such as '..', a root, or a directory-like path with no final file component, so src.file_name() returns None while copying sidecars.","commonSituations":"Script-generated configs with empty variable interpolation (e.g. \"binaries/${NAME}\" with NAME unset collapsing to a bare directory); typos; copying example configs with incomplete paths.","solutions":["Fix the externalBin entry to point at the sidecar file including its target-triple suffix, e.g. binaries/my-sidecar-x86_64-unknown-linux-gnu","Check generated tauri.conf.json for unset variables or copy-paste typos that strip the file name","Confirm each sidecar file actually exists with the -<target> suffix before bundling"],"exampleFix":"// tauri.conf.json — before\n\"externalBin\": [\"binaries/\"]\n\n// after\n\"externalBin\": [\"binaries/my-sidecar-x86_64-unknown-linux-gnu\"]","handlingStrategy":"validation","validationCode":"// validate externalBin entries before building\nconst last = (p) => p.split('/').pop() ?? '';\nconst bad = (config.bundle.externalBin ?? []).filter((p) => ['', '.', '..'].includes(last(p)));\nif (bad.length) throw new Error(`externalBin entries without a file name: ${bad.join(', ')}`);","typeGuard":"const hasSidecarFileName = (p) => { const seg = p.split('/').pop() ?? ''; return seg.length > 0 && seg !== '.' && seg !== '..'; };","tryCatchPattern":null,"preventionTips":["Every externalBin entry must name a file with the -<target-triple> suffix, never a bare directory","Validate generated tauri.conf.json with its JSON schema in CI before invoking the bundler","Fail template interpolation on unset variables instead of emitting empty path tails"],"tags":["sidecar","external-binary","config","path","tauri-bundler"],"backgroundTag":"invalid-path","analyzedSha":"52e4b6e71d8632a7e648f866c442e287ecddee34","analyzedAt":"2026-08-20T13:59:20.734Z","schemaVersion":2},"datasetVersion":"2026-08-31T22:30:34.772Z"}