Skip to content

Linux URL handler desktop entry quotes the Exec path, so xdg-open's generic fallback never launches the AppImage and OAuth callbacks open in the browser instead #16632

Description

@tunnckoCore

What happened

Desktop app on Linux (AppImage). Clicking Sign in (T3 Connect) opens the Clerk widget; choosing Google and completing the account picker ends in the browser asking to open the callback with xdg-open. Confirming does nothing useful: a second, empty browser window opens with the t3code:// URL, the in-app Clerk widget keeps spinning, and the user is never signed in. CLI login (bunx t3) works, so the account itself is fine.

Diagnosis

DesktopLinuxUrlHandler.ts writes the URL-handler desktop entry with a double-quoted Exec path (escapeDesktopEntryExecArgument wraps every value in "..."):

Exec="/home/<user>/.local/bin/t3code.AppImage" %U

The Desktop Entry spec allows this, but xdg-utils' shell fallback does not implement Exec quoting. On any desktop that xdg-open does not recognize (sway, Hyprland, i3, river, and so on) it takes the generic path, and search_desktop_file does:

command="$(get_key "${file}" "Exec" | first_word)"   # -> "/home/<user>/.local/bin/t3code.AppImage"  WITH the quotes
if command -v "$command" >/dev/null; then            # fails: no such command
    ... env "$command" "$@"
fi
# falls through to the $BROWSER fallback

command -v fails on the quoted string, the handler is skipped, and xdg-open falls back to $BROWSER / the default web browser, which is why a second browser window opens with the t3code:// URL. The running app never receives the callback, so the OAuth flow never completes. This is xdg-utils issues #151 and #279 (see Related); it has been open since 2019, so T3 cannot rely on it being fixed downstream.

Everything else in the registration chain was fine on this machine: the entry exists and points at the current AppImage, mimeinfo.cache lists it for x-scheme-handler/t3code, and xdg-mime query default x-scheme-handler/t3code returns com.t3tools.T3Code.desktop. Removing the quotes from Exec is the only change needed for xdg-open to launch it (verified below).

Desktops where xdg-open delegates to gio open or kde-open parse Exec correctly, which is why GNOME/KDE/COSMIC users are not hit and why #10354 was solved by the cache refresh alone.

Possible fix direction: only quote the Exec argument when it contains characters that need it (spaces, quotes, $, backticks, %), or point Exec at a small wrapper script at a quote-free path. Not patched locally.

Steps to reproduce

Standalone, no T3 needed (xdg-utils 1.2.1, with XDG_CURRENT_DESKTOP set to something xdg-open does not know, e.g. sway):

T=$(mktemp -d); mkdir -p "$T/share/applications" "$T/config"
printf '#!/bin/sh\necho "HANDLER: $*" >> %s/log\n' "$T" > "$T/handler.sh"
printf '#!/bin/sh\necho "BROWSER FALLBACK: $*" >> %s/log\n' "$T" > "$T/browser.sh"
chmod +x "$T"/*.sh
printf '[Desktop Entry]\nType=Application\nName=Quoted\nExec="%s" %%U\nMimeType=x-scheme-handler/q1;\n' "$T/handler.sh" > "$T/share/applications/q1.desktop"
printf '[Desktop Entry]\nType=Application\nName=Plain\nExec=%s %%U\nMimeType=x-scheme-handler/q2;\n'   "$T/handler.sh" > "$T/share/applications/q2.desktop"
printf '[Default Applications]\nx-scheme-handler/q1=q1.desktop\nx-scheme-handler/q2=q2.desktop\n' > "$T/config/mimeapps.list"
export XDG_DATA_HOME="$T/share" XDG_DATA_DIRS="$T/share" XDG_CONFIG_HOME="$T/config" BROWSER="$T/browser.sh" XDG_CURRENT_DESKTOP=sway
xdg-open q1://x; xdg-open q2://x; cat "$T/log"

Output:

BROWSER FALLBACK: q1://x
HANDLER: q2://x

In T3 Code:

  1. Install the Linux AppImage on a session whose desktop xdg-open treats as generic (sway here).
  2. Launch the app; it writes ~/.local/share/applications/com.t3tools.T3Code.desktop with the quoted Exec.
  3. Settings → Sign in → Google, finish the account picker in the browser.
  4. The browser prompts to open t3code://app/... with xdg-open. Confirm.
  5. A new browser window opens with the t3code:// URL; the app never receives it and the Clerk widget spins forever.

Version

0.0.46-nightly.20261004.2657 (tag v0.0.46-nightly.20261004.2657, commit efecd3cf)

Environment

NixOS, Linux 6.18.33 x86_64, sway (Wayland), xdg-utils 1.2.1, AppImage launched through nixpkgs appimage-run (bubblewrap FHS sandbox), default browser Helium 0.10.7.1 (Chromium based). Node v26.8.2.

Evidence

$ cat ~/.local/share/applications/com.t3tools.T3Code.desktop
[Desktop Entry]
Type=Application
Name=T3 Code (Nightly)
Exec="/home/<user>/.local/bin/t3code.AppImage" %U
Icon=/home/<user>/.local/share/icons/com.t3tools.T3Code.desktop.png
Terminal=false
NoDisplay=true
StartupNotify=false
MimeType=x-scheme-handler/t3code;

$ grep x-scheme-handler/t3code ~/.local/share/applications/mimeinfo.cache
x-scheme-handler/t3code=com.t3tools.T3Code.desktop;t3code-url-handler.desktop;

$ xdg-mime query default x-scheme-handler/t3code
com.t3tools.T3Code.desktop

$ BROWSER=/tmp/t/browser.sh xdg-open "t3code://app/triage-test"; cat /tmp/t/log
BROWSER FALLBACK CALLED: t3code://app/triage-test

# xdg-open 1.2.1, search_desktop_file():
command="$(get_key "${file}" "Exec" | first_word)"
if command -v "$command" >/dev/null; then

Related issues

Fix applied or workaround

Nothing changed on the machine by triage. Workaround for the user: an extra handler desktop entry with an unquoted Exec, set as the x-scheme-handler/t3code default in mimeapps.list. Because the app rewrites its own entry on every start, the workaround has to live in a separate file.

A second, machine-specific problem was also found and is not part of this issue: the user's default browser runs through a wrapper that sets HOME/XDG_DATA_HOME to a different directory, so the xdg-open it spawns does not see ~/.local/share/applications at all.

Filed by

Claude Fable 5.1 (Claude Code) via t3 triage, on behalf of the user.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions