Skip to content

fix(desktop): Windows .exe install fails on non-ASCII paths (#371)#517

Merged
rainxchzed merged 2 commits into
mainfrom
fix/371-windows-exe-install-non-ascii-path
May 5, 2026
Merged

fix(desktop): Windows .exe install fails on non-ASCII paths (#371)#517
rainxchzed merged 2 commits into
mainfrom
fix/371-windows-exe-install-non-ascii-path

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 5, 2026

Closes #371

Root cause

Desktop's Windows .exe install path uses java.awt.Desktop.getDesktop().open(file) which converts the file path to a URI internally. Paths with non-ASCII characters in the user's Windows username (Chinese, Korean, Cyrillic, etc.) or unusual filename glyphs fail URI conversion and surface as Unsupported URI content: <path>.

Fix

Hand off to ShellExecute via cmd /c start "" "<path>" instead. start takes the path verbatim through the system codepage and works regardless of charset. Same approach DesktopAppLauncher.launchWindowsApp already uses to launch installed apps. Desktop.open retained as fallback if cmd itself fails (defensive — shouldn't happen on Windows).

Test plan

  • Windows account with non-ASCII username (e.g. 用户 / пользователь / 사용자) — install any .exe from Details, no error.
  • Regression: Windows account with plain-ASCII username — install still works.
  • Regression: Windows .msi install (separate code path, not touched) — still works.
  • What's-new sheet on next 1.8.1 install shows the bullet in device language.

Summary by CodeRabbit

  • Bug Fixes
    • Windows installer now launches successfully on accounts with non-ASCII characters in the username or unusual characters in installation paths.
    • Retained fix: automatic updates no longer leave apps stuck on “Preparing to install.”

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e0239d52-0cad-4f2a-acb7-36981f3245f7

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf0d71 and 21ace00.

📒 Files selected for processing (14)
  • core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/16.json
✅ Files skipped from review due to trivial changes (13)
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/16.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt

Walkthrough

Prefers launching Windows .exe installers with cmd /c start "" <path>; on IOException falls back to Desktop.open(file) if supported or ProcessBuilder(file.absolutePath). Adds a localized "FIXED" release-note bullet across multiple whatsnew JSON files for version 16.

Changes

Windows Installer Launch & Localized Release Notes

Layer / File(s) Summary
Core Implementation
core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt
installWindows .exe branch now attempts ProcessBuilder("cmd", "/c", "start", "", file.absolutePath) and catches IOException.
Fallback Handling
core/data/.../DesktopInstaller.kt
On IOException logs a warning and falls back to Desktop.getDesktop().open(file) when Desktop.isDesktopSupported(); otherwise uses ProcessBuilder(file.absolutePath).
Localized Documentation
core/presentation/src/commonMain/composeResources/files/whatsnew/16.json, .../ar/16.json, .../bn/16.json, .../es/16.json, .../fr/16.json, .../hi/16.json, .../it/16.json, .../ja/16.json, .../ko/16.json, .../pl/16.json, .../ru/16.json, .../tr/16.json, .../zh-CN/16.json
Each localized whatsnew JSON adds a FIXED bullet noting the Windows installer launch no longer fails for non-ASCII usernames or unusual filename characters (version 16).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~18 minutes

Possibly related PRs

Poem

🐰 I hopped along the Windows trail,
Cmd start now leads where paths prevail.
No strange-name hurdle stops our run,
Installers start and users grin—what fun!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main fix: addressing Windows .exe installer failures on non-ASCII paths, which is the primary change across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/371-windows-exe-install-non-ascii-path

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rainxchzed rainxchzed force-pushed the fix/371-windows-exe-install-non-ascii-path branch from 6cf0d71 to 21ace00 Compare May 5, 2026 09:25
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt`:
- Around line 699-705: The fallback in the catch block handling cmd start (catch
(e: IOException)) can itself throw an IOException via
Desktop.getDesktop().open(file); wrap the fallback code inside its own try-catch
so any IOException from Desktop.open or ProcessBuilder.start is caught and
logged (use Logger.w or Logger.e) instead of propagating; update the block
around Desktop.getDesktop().open(file) /
ProcessBuilder(file.absolutePath).start() in DesktopInstaller.kt to catch and
log the fallback exception, referencing the existing catch (e: IOException)
handler and the Desktop.getDesktop().open(file) and ProcessBuilder(...) calls.

In `@core/presentation/src/commonMain/composeResources/files/whatsnew/fr/16.json`:
- Around line 34-35: Add the Windows installer fix bullet to each locale's
whatsnew 16.json that is missing it: open the 16.json files for ar, bn, hi, it,
ja, ko, ru, tr, and zh-CN and append the installer-fix bullet (the same entry
present in fr/es/pl: "Le lancement de l’installateur sous Windows ne plante plus
pour les comptes dont le nom d’utilisateur contient des caractères non-ASCII ou
des noms de fichier inhabituels."), replacing it with the correct localized
translation for each language and keeping the JSON array formatting consistent
with the existing entries in those 16.json files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 80f9c53a-392a-4c6f-a01a-075f5bb657e6

📥 Commits

Reviewing files that changed from the base of the PR and between 89d2a1a and 6cf0d71.

📒 Files selected for processing (14)
  • core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt
  • core/presentation/src/commonMain/composeResources/files/whatsnew/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ar/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/bn/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/es/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/fr/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/hi/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/it/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ja/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ko/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/pl/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/ru/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/tr/16.json
  • core/presentation/src/commonMain/composeResources/files/whatsnew/zh-CN/16.json

Comment on lines +699 to +705
} catch (e: IOException) {
Logger.w { "Failed to launch installer via cmd start: ${e.message}, falling back to Desktop.open" }
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(file)
} else {
ProcessBuilder(file.absolutePath).start()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fallback Desktop.open() can throw an uncaught IOException within the catch block.

Desktop.getDesktop().open(file) (line 702) declares throws IOException and will surface the same "Unsupported URI content" error for non-ASCII paths that motivated this fix. If cmd start somehow fails and Desktop.open also throws, the IOException escapes the catch (e: IOException) block unhandled — no log is emitted and the coroutine fails silently. Wrap the fallback in its own try-catch:

🛡️ Proposed fix
 } catch (e: IOException) {
     Logger.w { "Failed to launch installer via cmd start: ${e.message}, falling back to Desktop.open" }
-    if (Desktop.isDesktopSupported()) {
-        Desktop.getDesktop().open(file)
-    } else {
-        ProcessBuilder(file.absolutePath).start()
+    try {
+        if (Desktop.isDesktopSupported()) {
+            Desktop.getDesktop().open(file)
+        } else {
+            ProcessBuilder(file.absolutePath).start()
+        }
+    } catch (fallbackEx: IOException) {
+        Logger.e { "Fallback installer launch also failed: ${fallbackEx.message}" }
+        throw fallbackEx
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (e: IOException) {
Logger.w { "Failed to launch installer via cmd start: ${e.message}, falling back to Desktop.open" }
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(file)
} else {
ProcessBuilder(file.absolutePath).start()
}
} catch (e: IOException) {
Logger.w { "Failed to launch installer via cmd start: ${e.message}, falling back to Desktop.open" }
try {
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(file)
} else {
ProcessBuilder(file.absolutePath).start()
}
} catch (fallbackEx: IOException) {
Logger.e { "Fallback installer launch also failed: ${fallbackEx.message}" }
throw fallbackEx
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/data/src/jvmMain/kotlin/zed/rainxch/core/data/services/DesktopInstaller.kt`
around lines 699 - 705, The fallback in the catch block handling cmd start
(catch (e: IOException)) can itself throw an IOException via
Desktop.getDesktop().open(file); wrap the fallback code inside its own try-catch
so any IOException from Desktop.open or ProcessBuilder.start is caught and
logged (use Logger.w or Logger.e) instead of propagating; update the block
around Desktop.getDesktop().open(file) /
ProcessBuilder(file.absolutePath).start() in DesktopInstaller.kt to catch and
log the fallback exception, referencing the existing catch (e: IOException)
handler and the Desktop.getDesktop().open(file) and ProcessBuilder(...) calls.

@rainxchzed rainxchzed merged commit b00c9e3 into main May 5, 2026
1 check passed
@rainxchzed rainxchzed deleted the fix/371-windows-exe-install-non-ascii-path branch May 5, 2026 09:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Can't install any software

1 participant