{"record":{"id":"242a4566eff1ef98","repo":"rustdesk/rustdesk","slug":"root-update-failed-to-download","errorCode":null,"errorMessage":"[root-update] Failed to download: {}","messagePattern":"\\[root-update\\] Failed to download: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/updater.rs","lineNumber":629,"sourceCode":"    let private_tmp = String::from_utf8(private_tmp_output.stdout)\n        .map_err(|err| hbb_common::anyhow::anyhow!(\"[root-update] mktemp output error: {}\", err))?\n        .trim()\n        .to_owned();\n    if private_tmp.is_empty() {\n        bail!(\"[root-update] mktemp returned an empty download directory\");\n    }\n    {\n        use std::os::unix::fs::PermissionsExt;\n        std::fs::set_permissions(&private_tmp, std::fs::Permissions::from_mode(0o700))?;\n    }\n    let filename = dmg_url.split('/').last().unwrap_or(\"rustdesk.dmg\");\n    let file_path = std::path::PathBuf::from(format!(\"{}/{}\", private_tmp, filename));\n    let tmp_path = file_path.to_string_lossy().to_string();\n    // Download\n    let mut response = client.get(&dmg_url).send()?;\n    if !response.status().is_success() {\n        let _ = std::fs::remove_dir_all(&private_tmp);\n        bail!(\"[root-update] Failed to download: {}\", response.status());\n    }\n    // Create file exclusively (O_EXCL) and stream response directly into it\n    {\n        let mut file = std::fs::OpenOptions::new()\n            .write(true)\n            .create_new(true)\n            .open(&file_path)\n            .map_err(|e| { let _ = std::fs::remove_dir_all(&private_tmp); e })?;\n        std::io::copy(&mut response, &mut file)\n            .map_err(|e| { let _ = std::fs::remove_dir_all(&private_tmp); e })?;\n    }\n    log::info!(\"[root-update] Downloaded to {}\", tmp_path);\n    // Recheck active sessions before installing — download can take minutes\n    if !has_no_active_conns_ipc() {\n        if let Err(e) = std::fs::remove_dir_all(&private_tmp) {\n            log::warn!(\"[root-update] Failed to remove temp dir {}: {}\", private_tmp, e);\n        }\n        bail!(\"[root-update] Active session started during download, deferring update.\");","sourceCodeStart":611,"sourceCodeEnd":647,"githubUrl":"https://github.com/rustdesk/rustdesk/blob/91c9fccbb0f7bfe5f11644d5fbdec9b23fa10540/src/updater.rs#L611-L647","documentation":"The update .dmg is downloaded as root via a strict HTTP client from the allowlisted URL. If the server responds with a non-success HTTP status, the temp directory is removed and `check_update_as_root` bails with the status code. The update is aborted; the currently installed version keeps running.","triggerScenarios":"`client.get(&dmg_url).send()` yields `response.status().is_success() == false` — e.g. 404 because the version tag has no matching release asset, 403/429 rate-limiting from GitHub, 5xx from the server, or a proxy returning an error page.","commonSituations":"GitHub release asset not yet published/renamed for the detected version; GitHub rate limiting; corporate proxy blocking github.com; captive portals returning HTML error pages with 4xx/5xx statuses.","solutions":["Note the HTTP status in the message; a 404 means the dmg asset `rustdesk-<version>-<arch>.dmg` doesn't exist for that tag — verify the asset name on the release page.","For 403/429, wait for the GitHub rate limit to reset or use a mirrored source.","Check proxy/firewall rules allow reaching the release download host.","Retry the update later; the updater will attempt again on its schedule."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// pre-check that the release asset exists before triggering root update\n// curl -sIL '<dmg_url>' | head -1  -> expect HTTP/2 200","typeGuard":null,"tryCatchPattern":"match check_update_as_root() {\n    Err(e) if e.to_string().contains(\"Failed to download\") => {\n        log::warn!(\"update download failed, will retry later: {}\", e);\n        // schedule retry with backoff\n    }\n    other => other,\n}","preventionTips":["Verify the release tag has a matching rustdesk-<version>-<arch>.dmg asset before publishing updates.","Ensure proxies/firewalls allow the release download host.","Rely on the updater's retry schedule instead of immediate manual retries after 403/429."],"tags":["http","download","network","macos","updater"],"backgroundTag":"http-error-response","analyzedSha":"91c9fccbb0f7bfe5f11644d5fbdec9b23fa10540","analyzedAt":"2026-09-10T19:53:44.083Z","contentChangedAt":"2026-09-10T19:53:44.083Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}