{"record":{"id":"3793b3ad4badac25","repo":"tauri-apps/tauri","slug":"framework-path-should-have-framework-extension","errorCode":null,"errorMessage":"Framework path should have .framework extension: {}","messagePattern":"Framework path should have \\.framework extension: (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tauri-build/src/lib.rs","lineNumber":191,"sourceCode":"    if framework.ends_with(\".framework\") {\n      let src_path = Path::new(framework);\n      let src_name = src_path\n        .file_name()\n        .expect(\"Couldn't get framework filename\");\n      let dest_path = dest_dir.join(src_name);\n      copy_dir(src_path, &dest_path)?;\n      continue;\n    } else if framework.ends_with(\".dylib\") {\n      let src_path = Path::new(framework);\n      if !src_path.exists() {\n        return Err(anyhow::anyhow!(\"Library not found: {}\", framework));\n      }\n      let src_name = src_path.file_name().expect(\"Couldn't get library filename\");\n      let dest_path = dest_dir.join(src_name);\n      copy_file(src_path, &dest_path)?;\n      continue;\n    } else if framework.contains('/') {\n      return Err(anyhow::anyhow!(\n        \"Framework path should have .framework extension: {}\",\n        framework\n      ));\n    }\n    if let Some(home_dir) = dirs::home_dir() {\n      if copy_framework_from(&home_dir.join(\"Library/Frameworks/\"), framework, dest_dir)? {\n        continue;\n      }\n    }\n    if copy_framework_from(\"/Library/Frameworks/\".as_ref(), framework, dest_dir)?\n      || copy_framework_from(\"/Network/Library/Frameworks/\".as_ref(), framework, dest_dir)?\n    {\n      continue;\n    }\n  }\n  Ok(())\n}\n","sourceCodeStart":173,"sourceCodeEnd":209,"githubUrl":"https://github.com/tauri-apps/tauri/blob/2f1cd75b0f3fb72e6870d719bbfeadedcf8ca884/crates/tauri-build/src/lib.rs#L173-L209","documentation":"Entries in bundle.macOS.frameworks must be one of three shapes: a path ending in .framework (copied as a directory), a path ending in .dylib (copied as a file), or a bare framework name without '/' that is searched under the user's and system Library/Frameworks directories. An entry that contains a path separator but ends in neither extension is ambiguous and rejected before the standard-location search runs.","triggerScenarios":"bundle.macOS.frameworks contains a string with at least one '/' that does not end with '.framework' or '.dylib', e.g. 'vendor/MyFramework' or '/opt/local/lib/mylib'.","commonSituations":"Assuming any path to a framework directory is accepted; listing a plain library path while expecting the system-search behavior that is reserved for bare names; truncated extensions from manual editing.","solutions":["If the entry is a framework bundle, make the path end with '.framework' (e.g. vendor/MyFramework.framework)","If it is a dynamically linked library, point at the '.dylib' file explicitly","If the framework is installed system-wide (~/Library, /Library, /Network/Library), use its bare name with no slash so the standard locations are searched"],"exampleFix":"// before\n\"macOS\": { \"frameworks\": [\"vendor/Sparkle\"] }\n\n// after\n\"macOS\": { \"frameworks\": [\"vendor/Sparkle.framework\"] }  // or just \"Sparkle\"","handlingStrategy":"validation","validationCode":"// prebuild check: frameworks entries must be .framework, .dylib, or bare names\nconst cfg = JSON.parse(readFileSync('src-tauri/tauri.conf.json', 'utf8'))\nfor (const f of cfg.bundle?.macOS?.frameworks ?? []) {\n  if (f.includes('/') && !f.endsWith('.framework') && !f.endsWith('.dylib')) {\n    throw new Error(`invalid framework entry: ${f}`)\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Memorize the three valid shapes: .framework path, .dylib path, bare name","Use system frameworks by bare name so tauri-build searches standard locations","Validate the frameworks array in a config lint step"],"tags":["macos","frameworks","tauri-build","config"],"backgroundTag":null,"analyzedSha":"2f1cd75b0f3fb72e6870d719bbfeadedcf8ca884","analyzedAt":"2026-08-16T04:18:42.486Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}