{"record":{"id":"f6e037c8d5fb6a90","repo":"tauri-apps/tauri","slug":"is-not-a-file","errorCode":null,"errorMessage":"{:?} is not a file","messagePattern":"(.+?) is not a file","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tauri-build/src/lib.rs","lineNumber":50,"sourceCode":"mod codegen;\nmod manifest;\nmod mobile;\nmod static_vcruntime;\n\n#[cfg(feature = \"codegen\")]\n#[cfg_attr(docsrs, doc(cfg(feature = \"codegen\")))]\npub use codegen::context::CodegenContext;\n\npub use acl::{AppManifest, DefaultPermissionRule, InlinedPlugin};\n\nfn copy_file(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<()> {\n  let from = from.as_ref();\n  let to = to.as_ref();\n  if !from.exists() {\n    return Err(anyhow::anyhow!(\"{:?} does not exist\", from));\n  }\n  if !from.is_file() {\n    return Err(anyhow::anyhow!(\"{:?} is not a file\", from));\n  }\n  let dest_dir = to.parent().expect(\"No data in parent\");\n  fs::create_dir_all(dest_dir)?;\n  fs::copy(from, to)?;\n  Ok(())\n}\n\nfn copy_binaries(\n  binaries: ResourcePaths,\n  target_triple: &str,\n  path: &Path,\n  package_name: Option<&str>,\n) -> Result<()> {\n  for src in binaries {\n    let src = src?;\n    println!(\"cargo:rerun-if-changed={}\", src.display());\n    let file_name = src\n      .file_name()","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/tauri-apps/tauri/blob/2f1cd75b0f3fb72e6870d719bbfeadedcf8ca884/crates/tauri-build/src/lib.rs#L32-L68","documentation":"The sibling check in tauri-build's copy_file: the source path exists but is not a regular file (typically a directory), while the copy routines here need a concrete file. The Debug-formatted path in the message identifies the entry that resolved to a directory.","triggerScenarios":"A bundle.externalBin entry or a .dylib framework entry (anything funneled through copy_file) resolving to a directory - e.g. pointing externalBin at the folder containing sidecar binaries, or listing an assets directory where a single file is required.","commonSituations":"Confusing a directory of assets or binaries with one file in tauri.conf.json; glob/map expansion producing the parent directory; naming a macOS framework bundle directory where a .dylib file path is expected.","solutions":["Point the config entry at a concrete file path instead of its containing directory","For macOS frameworks use a .framework path (copied as a directory by design) or a bare name resolved from standard locations - not an arbitrary directory","If multiple assets are intended, use the bundle.resources glob/map forms the config supports rather than one directory path"],"exampleFix":"// before\n\"externalBin\": [\"binaries/\"]\n\n// after\n\"externalBin\": [\"binaries/mytool-x86_64-unknown-linux-gnu\"]","handlingStrategy":"validation","validationCode":"// prebuild check: every externalBin entry must be a regular file\nconst { statSync } = require('fs')\nconst cfg = JSON.parse(readFileSync('src-tauri/tauri.conf.json', 'utf8'))\nfor (const b of cfg.bundle?.externalBin ?? []) {\n  const s = statSync(`src-tauri/${b}`, { throwIfNoEntry: false })\n  if (s && !s.isFile()) throw new Error(`externalBin entry is not a file: ${b}`)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Point externalBin at concrete files with the target-triple suffix, never at directories","For directories of assets use bundle.resources globs/maps instead","Add a prebuild lint that stat()s every configured binary path"],"tags":["tauri-build","filesystem","config","sidecar"],"backgroundTag":null,"analyzedSha":"2f1cd75b0f3fb72e6870d719bbfeadedcf8ca884","analyzedAt":"2026-08-16T04:18:42.486Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}