{"record":{"id":"5f1af45659f4dfd0","repo":"tauri-apps/tauri","slug":"failed-to-read-resource-file-name","errorCode":null,"errorMessage":"failed to read resource file name","messagePattern":"failed to read resource file name","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tauri-bundler/src/bundle/windows/msi/mod.rs","lineNumber":1027,"sourceCode":"    // In some glob resource paths like `assets/**/*` a file might appear twice\n    // because the `tauri_utils::resources::ResourcePaths` iterator also reads a directory\n    // when it finds one. So we must check it before processing the file.\n    if added_resources.contains(&resource_path) {\n      continue;\n    }\n    added_resources.insert(resource_path.clone());\n\n    if settings.windows().can_sign() && should_sign(&resource_path)? {\n      try_sign(&resource_path, settings)?;\n    }\n\n    let resource_entry = ResourceFile::new(\n      resource_path,\n      Some(\n        resource\n          .target()\n          .file_name()\n          .expect(\"failed to read resource file name\")\n          .to_string_lossy()\n          .into_owned(),\n      ),\n    );\n\n    let target_path = resource.target();\n    let components_count = target_path.components().count();\n    let directories = target_path\n      .components()\n      .take(components_count - 1) // the last component is the file\n      .collect::<Vec<_>>();\n\n    let mut directory_entry = &mut root_resource_directory;\n\n    for directory in directories {\n      let directory_name = directory\n        .as_os_str()\n        .to_os_string()","sourceCodeStart":1009,"sourceCodeEnd":1045,"githubUrl":"https://github.com/tauri-apps/tauri/blob/460ec35447493200d64290dd7f015d5a91d0fd58/crates/tauri-bundler/src/bundle/windows/msi/mod.rs#L1009-L1045","documentation":"generate_resource_data builds a ResourceFile for each bundled resource and takes the leaf name of the resource's target path via file_name().expect(\"failed to read resource file name\"). When the configured target path has no final component (ends in a separator, is empty, or is '..'/root), the panic fires.","triggerScenarios":"Calling bundle_project with a bundle.resources entry whose target (path after '->' or the mapped destination) is a directory root or ends with '/'/'..', so resource.target().file_name() is None.","commonSituations":"Resources mapped like \"assets/->/\" or a target written with a trailing slash in tauri.conf.json, or resources entries that resolve to the drive root on Windows.","solutions":["Fix the resources entry so each target path names a file or ends in a concrete directory name (no trailing slash / '..').","Verify with the resolved target path printed in build logs which entry is malformed.","Guard the code by falling back to the source file name when the target has none."],"exampleFix":"// before (tauri.conf.json)\n\"resources\": [\"assets/*->/\"]\n\n// after\n\"resources\": [\"assets/*->assets/\"]","handlingStrategy":"validation","validationCode":"// validate resources targets in tauri.conf.json before bundling\nfor r in resources {\n  let t = r.target();\n  if t.file_name().is_none() {\n    return Err(anyhow!(\"resource target has no filename: {}\", t.display()));\n  }\n}","typeGuard":"fn has_filename(p: &std::path::Path) -> bool { p.file_name().is_some() }","tryCatchPattern":"// isolate the bundler in a subprocess; on this panic, point at the resources\n// entry whose target ends with a separator, is empty, or is '..'/root.","preventionTips":["Write resources targets with concrete directory names (no bare '/' targets)","Validate tauri.conf.json resources mappings in CI before running the build","Keep source and target resource paths free of '..' components"],"tags":["msi","windows","resources","path"],"backgroundTag":"path-has-no-filename","analyzedSha":"460ec35447493200d64290dd7f015d5a91d0fd58","analyzedAt":"2026-09-18T23:55:51.277Z","contentChangedAt":"2026-09-18T23:55:51.277Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}