gitbutlerapp/gitbutler · error

Clone remote URL contains unsupported control characters

Error message

Clone remote URL contains unsupported control characters

What it means

When building the repo-installer dump, clone_remote() reads the repository's remote URL and rejects URLs containing control characters (newline, carriage return, NUL). This is a safety check before the URL is embedded in archive metadata/scripts.

Solutions

  1. Inspect and fix the remote: `git remote -v`, then `git remote set-url origin <clean-url>`.
  2. Edit .git/config to remove embedded newlines/control characters in the remote URL.
  3. Re-add the remote: `git remote remove origin && git remote add origin <url>`.

Example fix

// before (.git/config)
[remote "origin"]
    url = https://example.com/repo.git
// after (fixed, no stray newline/control chars)
git remote set-url origin https://example.com/repo.git
Defensive patterns

Strategy: validation

Validate before calling

const url = execSync('git -C repo config --get remote.origin.url').toString();
if (/[\n\r\0]/.test(url)) throw new Error('Remote URL contains control characters; fix with git remote set-url');

Type guard

const isCleanUrl = (u) => typeof u === 'string' && u.length > 0 && !/[\n\r\0]/.test(u);

Prevention

When it happens

Trigger: Running `but-debug dump repo-installer` on a repository whose origin/clone remote URL string contains \n, \r, or \0 — typically from a corrupted or hand-edited .git/config or a path-derived remote.

Common situations: Manually edited .git/config with a wrapped line; remotes set from scripts without sanitization; weirdly named local directories joined into the remote path.

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 gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/0a68b37ae6f7a0bf. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-debug/src/command/dump/repo_installer.rs:159

    let remote_url = remote
        .url(gix::remote::Direction::Fetch)
        .with_context(|| format!("Clone remote '{name}' has no fetch URL"))?;
    let mut url = remote_url
        .to_bstring()
        .to_str()
        .context("Clone remote URL is not UTF-8")?
        .to_owned();
    if remote_url.scheme == gix::url::Scheme::File
        && remote_url.serialize_alternative_form
        && Path::new(&url).is_relative()
    {
        url = workdir
            .join(&url)
            .to_str()
            .context("Absolute clone remote path is not UTF-8")?
            .to_owned();
    }
    ensure!(
        !url.contains(['\n', '\r', '\0']),
        "Clone remote URL contains unsupported control characters"
    );
    Ok(CloneRemote { name, url })
}

fn head(repo: &gix::Repository) -> Result<(String, Option<String>)> {
    let head = repo.head().context("Could not read HEAD")?;
    let oid = head.id().context("Repository HEAD is unborn")?.to_string();
    let reference = head
        .referent_name()
        .map(|name| {
            name.as_bstr()
                .to_str()
                .context("HEAD reference name is not UTF-8")
                .map(ToOwned::to_owned)
        })
        .transpose()?;

View on GitHub (pinned to 58e5313667)