jdx/mise · error

semaphore closed

Error message

semaphore closed

What it means

The Go proxy version-info fetch spawns a task per version, each awaiting `sem.acquire_owned()`. `acquire_owned` only fails with `acquire closed`, meaning the semaphore was dropped while permits were still being awaited. The panic indicates the shared semaphore's lifetime ended before the spawned tasks finished — an internal concurrency bug.

Source

Thrown at src/backend/go.rs:751

}

async fn fetch_proxy_version_infos(
    proxies: &[GoProxy],
    path: &str,
    versions: &[String],
) -> Vec<VersionInfo> {
    let encoded = Arc::new(encode_module_path(path));
    let proxies = Arc::new(proxies.to_vec());
    let sem = Arc::new(Semaphore::new(GO_PROXY_VERSION_INFO_CONCURRENCY));
    let mut join_set = tokio::task::JoinSet::new();

    for version in versions {
        let proxies = proxies.clone();
        let encoded = encoded.clone();
        let sem = sem.clone();
        let version = version.clone();
        join_set.spawn(async move {
            let _permit = sem.acquire_owned().await.expect("semaphore closed");
            let endpoint = format!("{encoded}/@v/{version}.info");
            let info = query_proxy_version_metadata(proxies.as_slice(), &endpoint).await;
            (version, info)
        });
    }

    let mut times = BTreeMap::new();
    while let Some(result) = join_set.join_next().await {
        match result {
            Ok((version, ProxyVersionInfoResult::Found(info))) => {
                times.insert(version, info.time);
            }
            Ok((version, ProxyVersionInfoResult::NotFound | ProxyVersionInfoResult::Error)) => {
                times.insert(version, None);
            }
            Err(e) => warn!("proxy version info task panicked: {e}"),
        }
    }

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Update mise to the latest version where the semaphore lifetime is correct
  2. Retry the command — a transient cancellation path may have triggered it
  3. File a bug with the backtrace if reproducible

Example fix

// before
let sem = Arc::new(Semaphore::new(limit)); // dropped before tasks run
// after
let sem = Arc::new(Semaphore::new(limit));
let join_set = { /* spawn all tasks holding sem.clone() */ };
while let Some(res) = join_set.join_next().await { /* ... */ } // sem outlives tasks
Defensive patterns

Strategy: retry

Try / catch

mise ls-remote go || { echo 'transient proxy listing failure — retry'; mise ls-remote go; }

Prevention

When it happens

Trigger: `fetch_proxy_version_infos` (called by `fetch_proxy_versions` when listing Go module proxy versions) with a `sem` that is dropped or closed before `join_set` tasks complete — e.g. a refactor moves `sem` into a shorter-lived scope, or cancellation drops the semaphore early.

Common situations: Not reachable through user configuration; appears only with concurrency lifecycle bugs in the Go backend's parallel proxy queries, possibly under cancellation/timeouts during `mise install go` version listing.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/891b71fdad2df88f. Report an issue: GitHub.