Skip to content

Dialog API issue with file selection [bug]  #2153

Open

Description

@SebastianMonteroC

Describe the bug

I'm trying to use the dialog API in my Tauri app, specifically the FileDialogBuilder to select files but I'm having some issues with the way it's shown on MacOS (see picture).

It always appears in the top right corner of the screen, overlayed by the menu bar. This file selection window cannot be moved. I'm calling the dialog like this:

FileDialogBuilder::new().pick_folder();

I've used the dialog API for simple Yes/No boxes and it works well so unsure what could be the cause.

Screenshot 2023-07-10 at 10 48 22

Reproduction

I'm not doing anything special to reproduce this issue, just using to open the file dialog.

let file_selection = FileDialogBuilder::new().pick_folder();

Expected behavior

The file selection dialog should not appear on top of the screen, overlayed by the menu bar and blocked (cannot resize it or move it at all).

Platform and versions

Environment
  › OS: Mac OS 13.4.1 X64
  › Node.js: 18.13.0
  › npm: 9.3.0
  › pnpm: Not installed!
  › yarn: Not installed!
  › rustup: 1.25.1
  › rustc: 1.70.0
  › cargo: 1.70.0
  › Rust toolchain: 1.70.0-aarch64-apple-darwin 

Packages
  › @tauri-apps/cli [NPM]: 1.2.3
  › @tauri-apps/api [NPM]: Not installed!
  › tauri [RUST]: 1.4.0,
  › tauri-build [RUST]: 1.4.0,
  › tao [RUST]: 0.16.1,
  › wry [RUST]: 0.24.1,

App
  › build-type: bundle
  › CSP: unset
  › distDir: ../build
  › devPath: http://localhost:3000/
  › framework: React
  › bundler: Webpack

App directory structure
  ├─ .husky
  ├─ docker
  ├─ node_modules
  ├─ public
  ├─ scripts
  ├─ .github
  ├─ src-tauri
  ├─ build
  ├─ .git
  ├─ .vscode
  └─ src

Stack trace

No response

Additional context

No response

Activity

  1. dwosk commented on Jul 30, 2023

    @dwosk

    I'm also seeing this same behavior on macOS. The only workaround seems to be setting a parent window when building the file dialog, which is not ideal. It also makes using the builder a lot less ergonomic (since I need to pass around a window).

  2. transferred this issue fromtauri-apps/taurion Dec 5, 2024
  3. nicsheen commented on Sep 24, 2026

    @nicsheen

    Root cause, for anyone still hitting this: the dialog has no parent window, and rfd then picks one for itself.

    The plugin only applies a parent if one was set (desktop.rs, if let Some(parent) = d.parent), and the JS OpenDialogOptions has no parent field at all — so every dialog opened from JavaScript arrives with parent: None. rfd then guesses:

    // rfd-0.16.0/src/backend/macos/modal_future.rs
    let win = if let Some(win) = win {
        Some(win)
    } else {
        unsafe { app.mainWindow() }.or_else(|| app.windows().firstObject())
    };
    
    // if async exec is possible start sheet modal
    if unsafe { app.isRunning() } && win.is_some() {
        inner.begin_modal(&window.0, &completion);
    }

    and for the file panel begin_modal is beginSheetModalForWindow_completionHandler (file_dialog/panel_ffi.rs). So the panel is presented as a sheet on whatever that guess produced. mainWindow() is nil whenever the app isn't key — including immediately after a menu item is clicked — so it falls through to app.windows().firstObject(), which is arbitrary.

    A sheet attached to the wrong window looks exactly like the screenshot above: pinned somewhere unexpected and impossible to move, because sheets can't be moved.

    We hit a worse variant of the same thing on macOS 26.6.2: with the intended window full-screen, the sheet landed on a different window, which for a full-screen window means a different Space. The app was modally blocked by a panel that was nowhere on screen, and Escape was the only way out. Intermittent, because it depends on whether mainWindow() happens to be nil and on the ordering of app.windows().

    Workaround until #2563 lands — don't open the dialog from JS; wrap it in your own command and name the parent:

    #[tauri::command]
    pub async fn pick_open_path(window: tauri::Window) -> Option<String> {
        window
            .dialog()
            .file()
            .set_parent(&window)
            .blocking_pick_file()
            .and_then(|p| p.into_path().ok())
            .map(|p| p.to_string_lossy().into_owned())
    }

    (async so Tauri runs it off the main thread, which is what makes blocking_* safe here.) With the parent named, the sheet attaches to the invoking window every time — verified via the accessibility tree, where the panel now shows up as an AXSheet child of the right window.

    Message dialogs go through the same ModalFuture fallback, so they need .parent(&window) too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions