gitbutlerapp/gitbutler · error

Failed to create runtime

Error message

Failed to create runtime: {e}

What it means

This error is thrown when the Tokio async runtime cannot be constructed inside the dedicated update-check thread spawned by check_status_with_url. The function runs a current-thread Tokio runtime to perform its async HTTP update check from synchronous code; if tokio's runtime builder fails (e.g. due to resource exhaustion or a broken Tokio installation/feature set), the failure is wrapped with this message. It indicates an internal environment problem, not a network or server issue.

Solutions

  1. Check system resources (thread limits, ulimit -u, memory) and retry the update check after pressure subsides.
  2. Verify only one compatible tokio version is in the dependency graph (cargo tree -p tokio) and that features 'rt' and 'io' are enabled.
  3. Retry check_status; runtime creation failures are usually transient environment issues.
  4. If embedding this crate, consider running the update check on an already-existing async runtime instead of a fresh per-call runtime.

Example fix

// before
let runtime = tokio::runtime::Builder::new_current_thread()
    .enable_all()
    .build()
    .map_err(|e| anyhow::anyhow!("Failed to create runtime: {e}"))?;
// after (call from an async context instead of building a runtime per check)
let status = tokio::task::spawn_blocking(move || perform_update_check(...)).await??;
Defensive patterns

Strategy: try-catch

Try / catch

match but_update::check_status(app, &settings, &mut cache) {
    Err(e) if e.to_string().contains("Failed to create runtime") => {
        // environment/resource problem: log and degrade gracefully
        log::warn!("update check unavailable: {e:#}");
    }
    result => result?,
}

Prevention

When it happens

Trigger: Calling check_status or check_status_with_url when tokio::runtime::Builder::new_current_thread().enable_all().build() fails inside the spawned thread — typically from thread-local resource exhaustion, too many live runtimes, or a Tokio feature/build misconfiguration.

Common situations: System under severe thread/memory pressure when the update check runs; embedding in a process that restricts thread creation (rlimits, sandboxes, some CI containers); a Tokio dependency misconfiguration (io/timer drivers not enabled — though enable_all covers feature-gating, conflicting tokio versions can still break driver setup).

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/6e9272f33441ee89. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-update/src/check.rs:117

        arch: arch.to_string(),
        version: version.to_string(),
        app_name: app_name.to_string(),
        posthog_id: app_settings.telemetry.distinct_id_if_enabled(),
        install: install(),
    };

    let client = reqwest::Client::builder()
        .default_headers(headers)
        .timeout(REQUEST_TIMEOUT)
        .build()?;

    let url = url_override.unwrap_or(UPDATES_CHECK_URL).to_string();

    let result = std::thread::spawn(move || -> anyhow::Result<CheckUpdateStatus> {
        let runtime = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()
            .map_err(|e| anyhow::anyhow!("Failed to create runtime: {e}"))?;

        runtime.block_on(async {
            let response = client
                .post(url)
                .json(&request_body)
                .send()
                .await
                .map_err(|e| anyhow::anyhow!("Request failed: {e}"))?
                .error_for_status()
                .map_err(|e| anyhow::anyhow!("Server returned error: {e}"))?;

            let update_info = response
                .json::<CheckUpdateStatus>()
                .await
                .map_err(|e| anyhow::anyhow!("Failed to parse response: {e}"))?;

            Ok(update_info)
        })

View on GitHub (pinned to 58e5313667)