GitoxideLabs/gitoxide · error

configured beforehand

Error message

configured beforehand

What it means

Panic from `child.stderr.take().expect("configured beforehand")` in the blocking file transport's `handshake`. When SSH is used, the code asserts the spawned child has a piped stderr, which requires `Command::stderr(Stdio::piped())` to have been called before `spawn()`. The panic fires only if the spawn setup forgot to configure stderr — an internal setup invariant, not user input.

Solutions

  1. Ensure the code path that spawns the SSH child calls `.stderr(Stdio::piped())` (and stdout/stdin) before `spawn()`.
  2. Upgrade gix-transport to a version where SSH spawn configures all pipes.
  3. If using a custom `GIT_SSH_COMMAND` wrapper, verify the failure isn't from a locally modified transport build.

Example fix

// before
let child = cmd.spawn()?; // stderr not piped
// after
let child = cmd.stderr(Stdio::piped()).stdout(Stdio::piped()).stdin(Stdio::piped()).spawn()?;
Defensive patterns

Strategy: try-catch

Try / catch

std::panic::catch_unwind(AssertUnwindSafe(|| handshake(...)))
    .map_err(|_| anyhow::anyhow!("SSH handshake panicked: stderr pipe not configured"))?

Prevention

When it happens

Trigger: Performing a handshake over an SSH remote (ssh_kind present) when the child process was spawned without `Stdio::piped()` for stderr, e.g. in a patched build or after changes to the spawn code path.

Common situations: Custom ssh_cmd wrappers or forks that alter process spawning; regressions in gix-transport after refactoring the SSH invocation.

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 GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/78acfe562d3167b8. Report an issue: GitHub.

Appendix: source

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

                let (cmd, cmd_name) = self.prepare_fallback_command(service);
                let mut cmd = into_std_command(cmd);
                gix_features::trace::debug!(command = ?cmd, "gix_transport::SpawnProcessOnDemand (fallback)");
                cmd.spawn().map_err(|err| client::Error::InvokeProgram {
                    source: err,
                    command: cmd_name,
                })?
            }
            Err(err) => {
                return Err(client::Error::InvokeProgram {
                    source: err,
                    command: cmd_name,
                });
            }
        };
        let stdout: Box<dyn std::io::Read + Send> = match ssh_kind {
            Some(ssh_kind) => Box::new(supervise_stderr(
                ssh_kind,
                child.stderr.take().expect("configured beforehand"),
                child.stdout.take().expect("configured"),
            )),
            None => Box::new(child.stdout.take().expect("stdout configured")),
        };
        self.connection = Some(Connection::new_for_spawned_process(
            stdout,
            child.stdin.take().expect("stdin configured"),
            self.desired_version,
            self.path.clone(),
            self.trace,
        ));
        self.child = Some(child);
        self.connection
            .as_mut()
            .expect("connection to be there right after setting it")
            .handshake(service, extra_parameters)
    }

View on GitHub (pinned to e73179060b)