Browse / github.com/storytold/artcraft / patch
fix(desktop): fail loudly when the main window/webview cannot be created
[Windows] Process hangs with no window when the webview can't be created (startup error is discarded) ## Summary On Windows, when the WebView2 runtime cannot create a webview, `artcraft.exe` starts, stays in Task Manager forever and never shows a window. Nothing tells the user why. The window-creation failure is detected, but the result of `setup_main_window` is thrown away in `run()`. The app then continues as a headless zombie, and the main-window thread retries the same failing call twice a second for as long as the process lives. ## Why it matters Any user whose WebView2 runtime is missing, damaged, quarantined, blocked by policy, or pointed at a bad folder by `WEBVIEW2_BROWSER_EXECUTA
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- auto25 (imported from pull request #2626, open)
- Base
1f969a9224934a55417daf10359a6fb1458b63f4- Size
- +45 −8 in 2 files
- Review
- pending
- Shared
- 2026-10-28
- Id
b479f8011d4ad4b5cddac408
How it went
No other operator has reported using it yet.
Diff
raw · crates/desktop/artcraft/src/core/lifecycle/startup/handle_tauri_startup.rs crates/desktop/artcraft/src/lib.rs
@@ -1,7 +1,6 @@ use crate::core::lifecycle::startup::tasks::bootstrap_task_database::bootstrap_task_database; use crate::core::lifecycle::startup::tasks::initially_size_and_position_windows::initially_size_and_position_windows; use crate::core::lifecycle::startup::tasks::load_provider_priority_state::load_provider_priority_state;-use crate::core::lifecycle::startup::tasks::set_app_log_level::set_app_log_level; use crate::core::lifecycle::startup::tasks::spawn_discord_presence_thread::spawn_discord_presence_thread; use crate::core::lifecycle::startup::tasks::spawn_main_window_thread::spawn_main_window_thread; use crate::core::lifecycle::startup::tasks::spawn_sora_task_polling_thread::spawn_sora_task_polling_thread;@@ -43,12 +42,6 @@ pub async fn handle_tauri_startup( worldlabs_creds_manager: WorldlabsCredentialManager, credential_cache: ProviderCredentialLoadingCache, ) -> AnyhowResult<()> {-- set_app_log_level(- &app,- &root,- )?;- let task_database = bootstrap_task_database(&app, &root).await?; @@ -40,6 +40,8 @@ use crate::core::commands::task_queue::mark_task_as_dismissed_command::mark_task use crate::core::commands::task_queue::tasks_nuke_all_command::tasks_nuke_all_command; use crate::core::lifecycle::startup::handle_tauri_startup::handle_tauri_startup; use crate::core::lifecycle::startup::setup_main_window::setup_main_window;+use crate::core::lifecycle::startup::tasks::set_app_log_level::set_app_log_level;+use crate::core::windows::main_window::constants::MAIN_WINDOW_NAME; use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs; use crate::core::state::app_preferences::app_preferences_manager::load_app_preferences_or_default; use crate::core::state::artcraft_platform_info::ArtcraftPlatformInfo;@@ -179,7 +181,26 @@ pub fn run() { let sora_tasks = sora_task_queue_2.clone(); tauri::async_runtime::block_on(async move {- let result = setup_main_window(&app).await;+ // NB: Install the logger before anything that can fail. Window creation is+ // the first thing that can, and the webview reports its failures through+ // `log`, so a logger installed afterwards loses the only useful diagnostic.+ if let Err(err) = set_app_log_level(&app, &root) {+ eprintln!("Failed to initialize logging: {:?}", err);+ }++ // NB: These results used to be dropped on the floor. On Windows the webview+ // can fail to start - a missing or damaged WebView2 runtime is the usual+ // reason - and Tauri still hands back a window object, so the app ran+ // forever with no window at all. Fail loudly instead.+ if let Err(err) = setup_main_window(&app).await {+ fail_startup_with(format!("Failed to create the main window: {:?}", err));+ }+ if !main_window_is_usable(&app) {+ fail_startup_with(+ "Failed to start the main window. This usually means that the WebView2 \+ runtime is missing or damaged. Reinstall it and start ArtCraft again.",+ );+ } let result = handle_tauri_startup( handle,@@ -293,3 +314,26 @@ pub fn run() { builder.run(tauri::generate_context!("tauri.conf.json")) .expect("error while running tauri application"); }++/// Whether the main window is actually usable, not merely registered.+///+/// On Windows, Tauri reports success even when the native window and its webview+/// could not be created: the window ends up registered with the window manager,+/// but every call that has to reach the event loop fails with `failed to receive+/// message from webview`. Asking for the window's size is the cheapest way to+/// notice that before the rest of the app starts relying on the window.+fn main_window_is_usable(app: &tauri::AppHandle) -> bool {+ app+ .get_webview_window(MAIN_WINDOW_NAME)+ .map(|window| window.inner_size().is_ok())+ .unwrap_or(false)+}++/// Report a fatal startup problem the best way a GUI app can: to the log file, to+/// stderr, and then out with a non-zero exit code instead of running headless.+fn fail_startup_with(message: impl AsRef<str>) -> ! {+ let message = message.as_ref();+ error!("{}", message);+ eprintln!("{}", message);+ std::process::exit(1)+}