GitoxideLabs/gitoxide · error

git daemon repository paths must not contain NUL or LF

Error message

git daemon repository paths must not contain NUL or LF

What it means

The `git://` (git daemon) transport rejects repository paths containing NUL (`\0`) or LF (`\n`) bytes before building the request, because these bytes are forbidden in the daemon protocol's path argument. The connection is refused locally with `InvalidInput` before any network I/O.

Solutions

  1. Trim whitespace/newlines from the URL/path string before connecting
  2. Reject or sanitize path components containing NUL/LF before constructing the `git://` URL
  3. Log/validate the URL at the application boundary

Example fix

// before
let url = format!("git://host{}
"); // trailing LF from input
// after
let url = format!("git://host{}").trim().to_string();
Defensive patterns

Strategy: validation

Validate before calling

fn validate_git_daemon_path(path: &str) -> Result<(), String> {
    if path.bytes().any(|b| b == b'\0' || b == b'\n') {
        Err("git:// path contains NUL or LF".into())
    } else { Ok(()) }
}

Try / catch

match gix::connect(url, gix::protocol::transport::Protocol::Git) {
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
        eprintln!("sanitize the git:// URL: {e}");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling `gix::connect()` (or the git client's `request()`/`connect`) with a `git://` URL whose expanded path contains NUL or newline bytes.

Common situations: URLs built by concatenation that accidentally embed a trailing newline (e.g. from a file or env var read without trimming); binary garbage in programmatically constructed URLs.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at gix-transport/src/client/git/mod.rs:38

    pub(in crate::client) mode: ConnectMode,
}

mod message {
    use bstr::{BString, ByteVec};

    use crate::{Protocol, Service};

    pub fn connect(
        service: Service,
        desired_version: Protocol,
        path: &[u8],
        virtual_host: Option<&(String, Option<u16>)>,
        extra_parameters: &[(&str, Option<&str>)],
    ) -> std::io::Result<BString> {
        let path = gix_url::expand_path::for_shell(path.into());
        let is_forbidden = |byte| matches!(byte, b'\0' | b'\n');
        if path.iter().copied().any(is_forbidden) {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "git daemon repository paths must not contain NUL or LF",
            ));
        }
        if virtual_host.is_some_and(|(host, _)| host.bytes().any(is_forbidden)) {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                "git daemon virtual hosts must not contain NUL or LF",
            ));
        }

        let mut out = bstr::BString::from(service.as_str());
        out.push(b' ');
        out.extend_from_slice(&path);
        out.push(0);
        if let Some((host, port)) = virtual_host {
            out.push_str("host=");
            out.extend_from_slice(host.as_bytes());

View on GitHub (pinned to e73179060b)