GitoxideLabs/gitoxide · error

named threads with small stack work on all platforms

Error message

named threads with small stack work on all platforms

What it means

Panic from `std::thread::Builder::spawn().expect("named threads with small stack work on all platforms")` inside `supervise_stderr`, which launches a helper thread to forward a child process's stderr during a transport handshake. The expect asserts that spawning a small-stack named thread always succeeds; failure means the OS refused to create the thread (resource exhaustion or platform limitation).

Solutions

  1. Raise the thread/process limit (`ulimit -u`) or container PID limit and retry.
  2. Free resources (reduce concurrent connections) before retrying the fetch/push.
  3. If on an exotic platform, run the blocking transport on a default-size stack, or use the async transport instead.
  4. Retry the operation once resources are available — it is transient.
Defensive patterns

Strategy: retry

Validate before calling

if rlimit::getrlimit(rlimit::Resource::NPROC).map(|(_, hard)| hard == 0).unwrap_or(false) {
    anyhow::bail!("thread/process limit too low for transport threads");
}

Try / catch

match std::panic::catch_unwind(AssertUnwindSafe(|| transport.handshake(version))) {
    Ok(r) => r?,
    Err(_) => { wait_for_resources(); anyhow::bail!("handshake thread spawn failed; retry after raising limits"); }
}

Prevention

When it happens

Trigger: Calling a blocking transport `handshake` (spawning `git upload-pack`/`receive-pack` or an SSH command) when thread creation fails — e.g. hitting RLIMIT_NPROC/`ulimit -u`, out of memory, or a platform where the small stack size is rejected.

Common situations: Containers with very low thread/process limits, deeply recursive workloads exhausting threads, or unusual/embedded targets with restrictive pthread settings.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/f8db95d50935131d. Report an issue: GitHub.

Appendix: source

Thrown at gix-transport/src/client/blocking_io/file.rs:262

        .name("supervise ssh stderr".into())
        .stack_size(128 * 1024)
        .spawn(move || -> std::io::Result<()> {
            let mut process_stderr = std::io::stderr();
            for line in std::io::BufReader::new(stderr).byte_lines() {
                let line = line?;
                match ssh_kind.line_to_err(line.into()) {
                    Ok(err) => {
                        send.send(err).ok();
                    }
                    Err(line) => {
                        process_stderr.write_all(&line).ok();
                        writeln!(&process_stderr).ok();
                    }
                }
            }
            Ok(())
        })
        .expect("named threads with small stack work on all platforms");
    ReadStdoutFailOnError { read: stdout, recv }
}

impl client::blocking_io::Transport for SpawnProcessOnDemand {
    fn handshake<'a>(
        &mut self,
        service: Service,
        extra_parameters: &'a [(&'a str, Option<&'a str>)],
    ) -> Result<SetServiceResponse<'_>, client::Error> {
        let (cmd, ssh_kind, cmd_name) = self.prepare_command(service)?;
        let envs = std::mem::take(&mut self.envs);
        let into_std_command = |mut cmd: gix_command::Prepare| {
            cmd.stdin = Stdio::piped();
            cmd.stdout = Stdio::piped();

            let mut cmd = std::process::Command::from(cmd);
            for env_to_remove in ENV_VARS_TO_REMOVE {
                cmd.env_remove(env_to_remove);

View on GitHub (pinned to e73179060b)