Skip to content

fix(linux): deliver deep links and opened files to the app - #8976

Merged
t8y2 merged 1 commit into
t8y2:mainfrom
anandghegde:fix/linux-desktop-entry-field-code
Sep 13, 2026
Merged

t8y2 merged 1 commit into
t8y2:mainfrom
anandghegde:fix/linux-desktop-entry-field-code

Conversation

@anandghegde

@anandghegde anandghegde commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

The bug

On Linux, DBX is advertised as the handler for dbx:// links and for .sql files, but never receives either. Both silently do nothing:

  • dbx://connection/new?... — open a connection from a link
  • dbx://settings/ai/new?... — add an AI provider
  • dbx://open — bring the app to the front
  • double-clicking a .sql file in a file manager

Why

  1. The handler is advertised. tauri-bundler builds MimeType= from both the deep-link schemes and the file associations in tauri.conf.json, so the desktop entry claims x-scheme-handler/dbx and application/sql and the desktop routes those to DBX.

  2. But nothing is passed. The bundler's stock template emits a bare Exec={{exec}} with no field code (main.desktop). Per the Desktop Entry spec, a handler receives its URL or file only if Exec= carries a field code. Without one the argument is discarded rather than appended to argv.

  3. Both entry points read argv. The startup path (lib.rs#L1629) uses std::env::args().skip(1), and the single-instance callback (lib.rs#L1416) works off the args it is handed. With nothing in argv, connection_deep_links_from_args, ai_config_deep_links_from_args, sql_file_paths_from_args and db_file_paths_from_args all return empty.

Changes

New src-tauri/linux/DBX.desktop — the bundler's stock template with a field code added:

Exec={{exec}} %U

%U rather than %f/%F because a single Exec= line serves both a URL scheme and file associations: %f cannot carry a dbx:// URL, while %U covers both and the arg handlers already accept multiple values.

normalize_launch_path_arg (src-tauri/src/commands/launch_args.rs) — required by that choice. With %U, file managers such as Nautilus and Dolphin hand over file:// URLs instead of plain paths, and the path parsers treated their argument as a literal path. I checked the current behaviour by compiling sql_file_path_from_arg as-is:

/tmp/q.sql                 -> Some("/tmp/q.sql")
file:///tmp/q.sql          -> Some("/home/user/file:///tmp/q.sql")     <- joined onto cwd
file:///tmp/my%20query.sql -> Some("/home/user/file:///tmp/my%20query.sql")

"file:///tmp/q.sql" is not absolute, so it was joined onto the cwd and became a path that cannot exist. The new helper decodes file:// URLs (including percent-escapes and a localhost authority), returns every other argument untouched, and rejects file:// URLs pointing at another host. sql_file_path_from_arg and db_file_path_from_arg now go through it.

src-tauri/tauri.conf.json — points deb and rpm at the template, and sets bundle.category, which was unset and left Categories= empty so the app files under no menu category.

Rendered entry:

[Desktop Entry]
Categories=Development;
Comment=DBX
Exec=DBX %U
StartupWMClass=DBX
Icon=DBX
Name=DBX
Terminal=false
Type=Application
MimeType=application/sql;x-scheme-handler/dbx

Notes

  • StartupWMClass deliberately keeps bare {{exec}} with no field code — it must match the actual WM class or window-to-launcher grouping breaks.
  • Name keeps {{name}} and productName is untouched, so the binary name, WM class and updater artifacts are unaffected.
  • DeveloperTool maps to Development; on freedesktop and public.app-category.developer-tools on macOS.
  • deb + rpm, AppImage free — both are published and share the bug; AppImage inherits the deb template because its bundler calls debian::generate_data.

Separate observation, not addressed here

The .db / .sqlite / .sqlite3 / .duckdb association declares no mimeType, so unlike .sql it contributes nothing to MimeType= and those files are not associated with DBX on Linux at all. db_file_paths_from_args still benefits from this PR (paths arrive when DBX is launched with one), but wiring up the association needs a MIME type plus a shared-mime-info package, which felt like a separate change. Happy to open an issue.

Verification

make cargo-test-fast equivalent (cargo test --no-default-features --features sqlite-bundled):

test commands::launch_args::tests::passes_plain_paths_through_unchanged ... ok
test commands::launch_args::tests::decodes_local_file_urls ... ok
test commands::launch_args::tests::percent_decodes_file_urls ... ok
test commands::launch_args::tests::rejects_file_urls_on_another_host ... ok
test commands::launch_args::tests::linux_desktop_entry_forwards_urls_and_files ... ok
test commands::launch_args::tests::bundle_config_uses_the_linux_desktop_entry ... ok
test commands::external_sql::tests::accepts_sql_file_args_passed_as_file_urls ... ok
test commands::external_db::tests::accepts_db_file_args_passed_as_file_urls ... ok

The full commands::external suite also passes (28 passed, 0 failed), so the parser change is not a regression.

Two of the new tests are file-content checks on the shipped desktop entry, per the guidance in CONTRIBUTING that these suit shipped artifacts and cross-runtime contracts: the Exec= field code is precisely the contract that puts links and files into std::env::args(), and it is invisible to behaviour tests. One pins that the entry keeps a URL field code and that StartupWMClass stays bare; the other pins that the bundler is actually pointed at the template, so the fix cannot be silently unwired.

Also: rustfmt clean under the repo's max_width = 120 / use_small_heuristics = "Max", and tauri.conf.json validates against the official schema for the resolved tauri 2.11.5 — BundleConfig, DebConfig and RpmConfig all set additionalProperties: false, so that confirms category and desktopTemplate are recognized keys rather than silently ignored.

If you add a preview/build label I'm glad to help confirm on the resulting .deb that a dbx:// link and a .sql double-click both reach the app.

No linked issue: blank issues are disabled and the bug report template is database-specific, so the analysis lives here. Happy to file one in whatever form you prefer.

On Linux the bundled desktop entry advertises DBX as the handler for
dbx:// links and for .sql files, but never passes either to the app, so
both silently do nothing.

tauri-bundler derives MimeType from the deep-link schemes and the file
associations, so the desktop routes dbx:// links and .sql files to DBX.
Its stock template then emits a bare `Exec={{exec}}` with no field code,
so the URL or path is dropped instead of being appended to argv. Both
entry points in lib.rs read std::env::args(): the startup path at
line 1629 and the single-instance callback at line 1416. With nothing in
argv, connection_deep_links_from_args, ai_config_deep_links_from_args and
sql_file_paths_from_args all return empty, so dbx://connection/new,
dbx://settings/ai/new, dbx://open and .sql file opening are lost.

Add a custom desktopTemplate using `Exec={{exec}} %U`. %U rather than %f
because one Exec line serves both a URL scheme and file associations, and
the arg handlers already accept multiple values.

%U means a file manager may hand over file:// URLs rather than plain
paths, and the path parsers treated their argument as a literal path:
"file:///tmp/q.sql" is not absolute, so it was joined onto the cwd and
became "/cwd/file:///tmp/q.sql". Add normalize_launch_path_arg to decode
file:// URLs, including percent-escapes, and use it from both parsers.

Also set bundle.category, which was unset and left Categories= empty, so
the app files under no menu category.

Wired into deb and rpm since both are published; AppImage inherits the
deb template because its bundler reuses debian::generate_data.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01XWr9UtfXxdTdfHMydFh4V8

@t8y2 t8y2 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks — reviewed and approved. Verified against the pinned toolchain (@tauri-apps/cli 2.11.4): the stock main.desktop template indeed ships without a field code, deb/rpm both honor desktopTemplate, and AppImage inherits it via debian::generate_data, so the Exec={{exec}} %U template reaches all three Linux targets. %U is the right code per the freedesktop spec, and normalize_launch_path_arg correctly handles the file:// form file managers pass under it. Deep-link dbx:// args bypass the helper and macOS/Windows argv behavior is unchanged. One note for our release notes: bundle.category is cross-platform, so the macOS Info.plist also gains LSApplicationCategoryType — harmless for direct distribution. The CI run is waiting on first-time contributor workflow approval on our side.

@t8y2
t8y2 merged commit f6e5b3a into t8y2:main Sep 13, 2026
16 checks passed
@t8y2

t8y2 commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Thanks for the contribution! Merged in f6e5b3a, will be released in the next version.

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

Labels

area/desktop Desktop application or Tauri shell bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants