Hmbown/CodeWhale · error
remote url contains control characters
Error message
remote url contains control characters
What it means
validate_git_remote_url rejects URLs containing control characters (per char::is_control). Control bytes in a URL can smuggle newlines, escapes, or terminal sequences into git commands and logs, so they are refused before any clone.
Solutions
- Trim and strip control characters (or reject) at input time before validation.
- Re-enter the URL as a single clean line; copy it from the forge's official clone widget.
- If reading from a file, normalize line endings and split on whitespace, taking one token.
- Log the sanitized value when reporting the failure; never echo raw control bytes.
Example fix
// before
let url = format!("https://github.com/org/repo.git\n{}");
validate_git_remote_url(&url)?;
// after
let url = raw.trim().chars().filter(|c| !c.is_control()).collect::<String>();
validate_git_remote_url(&url)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_control_chars(s: &str) -> bool {
s.chars().any(char::is_control)
} Type guard
fn clean_url(s: &str) -> Option<String> {
let cleaned: String = s.trim().chars().filter(|c| !c.is_control()).collect();
(cleaned == s.trim()).then_some(cleaned)
} Try / catch
match validate_git_remote_url(raw) {
Err(e) if e.to_string().contains("control characters") => {
let sanitized = raw.trim().chars().filter(|c| !c.is_control()).collect::<String>();
eprintln!("URL contained control chars; sanitized: {sanitized}");
}
other => { /* ... */ }
} Prevention
- Normalize CRLF and strip trailing newlines when reading URLs from files.
- Paste URLs as a single line; avoid terminal selection that spans wraps.
- Sanitize or reject control bytes at every input boundary, not just for remotes.
- Never log raw unvalidated URL input.
When it happens
Trigger: Calling validate_git_remote_url / safe_git_remote_url / clone_repository with a string containing e.g. \n, \r, \t, or other C0/C1 control bytes — typically from multiline paste, binary-corrupted config, or concatenation with log output.
Common situations: Pasting a URL from a terminal where a line wrap introduced a newline; reading a value from a file that kept a trailing \r (CRLF); string building that accidentally joined a URL with log lines.
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
- Pet Runtime input requires a plain HTTP loopback IP origin…
- remote url is empty or oversized
- remote url is not a supported forge
- remote url must not embed userinfo
- remote url must not start with '-'
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/0a16aee07e9cfb45.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/cloud_dispatch.rs:414
/// and network remotes that do not classify as a supported forge. Local
/// path remotes (offline fixtures) are allowed when they do not start
/// with `-` and carry no userinfo.
pub fn safe_git_remote_url(raw: &str) -> bool {
validate_git_remote_url(raw).is_ok()
}
/// Classify and validate `job.remote_url` before any `git clone` or
/// sandbox clone. Returns the trimmed URL on success.
pub fn validate_git_remote_url(raw: &str) -> Result<String> {
let url = raw.trim();
if url.is_empty() || url.len() > MAX_REMOTE_BYTES {
bail!("remote url is empty or oversized");
}
if url.starts_with('-') {
bail!("remote url must not start with '-'");
}
if url.chars().any(char::is_control) {
bail!("remote url contains control characters");
}
if remote_has_userinfo(url) {
bail!("remote url must not embed userinfo");
}
if looks_like_network_git_url(url) && classify_url(url).is_none() {
bail!("remote url is not a supported forge");
}
Ok(url.to_string())
}
/// Display form of a remote: userinfo is never printed.
pub fn redact_remote_url(raw: &str) -> String {
redact_url_userinfo(raw)
}
fn remote_has_userinfo(url: &str) -> bool {
if let Ok(parsed) = reqwest::Url::parse(url) {
return !parsed.username().is_empty() || parsed.password().is_some();View on GitHub (pinned to 73e0f67d83)