Hmbown/CodeWhale · error
Cloud agent repository clone failed
Error message
Cloud agent repository clone failed (HTTP {status}). What it means
Thrown by `clone_repository` when the sandbox toolbox HTTP endpoint `POST git/clone` returns a non-2xx status. The dispatcher already validated the remote URL and sent `{url, path}` to the sandbox; the sandbox itself refused or failed the clone. The provider HTTP status is surfaced in the message so the developer can distinguish auth (401/403), bad request (400/422), and server-side failures (5xx).
Solutions
- Read the HTTP status in the message: 401/403 means supply repository credentials the sandbox can use (deploy key/token in the remote URL via validate_git_remote_url-accepted forms); 404 means the repo URL is wrong or private.
- Verify the repo URL is reachable and the correct branch/ref exists; test `git ls-remote <url>` locally.
- Retry the job if the status is 5xx — the sandbox toolbox may have been temporarily unhealthy.
- Ensure the target clone path inside SANDBOX_WORKSPACE is not pre-occupied; a stale sandbox from a prior run may still hold the path — tear it down and retry.
Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: confirm the repo is reachable before dispatching the cloud job
let out = std::process::Command::new("git")
.args(["ls-remote", repo_url])
.output()?;
if !out.status.success() {
anyhow::bail!("repo URL unreachable before cloud dispatch: {}", String::from_utf8_lossy(&out.stderr));
} Try / catch
match result {
Err(e) if e.to_string().contains("clone failed (HTTP 4") => show("check repo URL / credentials for the sandbox"),
Err(e) if e.to_string().contains("clone failed (HTTP 5") => retry_with_backoff(job, 2),
other => other,
} Prevention
- Validate the remote URL and access with `git ls-remote` before dispatching.
- Provision sandbox credentials (deploy key/PAT) for private repos ahead of the clone.
- Use a clean sandbox per job so a stale clone path cannot collide.
- Check status codes: 4xx = URL/credentials problem, 5xx = provider problem.
When it happens
Trigger: Any cloud-agent run that clones a repository into the sandbox when the toolbox `git/clone` call fails: invalid or unauthorized repo URL (private repo without credentials the sandbox can use), nonexistent repo/ref, unwritable target path inside the sandbox, or toolbox returning 5xx.
Common situations: Cloning a private repository the sandbox token cannot read; typo'd or renamed repo URL; the target `path` already exists or is not writable; large monorepo clone timing out at the toolbox; sandbox disk quota exceeded (507/5xx).
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Cloud agent harness execution failed
- Cloud agent create failed
- Cloud agent harness exited with code
- cloud agent label apply failed
- Cloud agent produced an empty patch; refusing to open a PR.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/a2ed6af64b83bbcd.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/cloud_dispatch.rs:1636
if response.status().as_u16() == 404 {
bail!("Cloud agent sandbox disappeared before it was ready.");
}
}
}
std::thread::sleep(READY_POLL_INTERVAL);
}
bail!("Cloud agent sandbox was not ready in time.");
}
fn clone_repository(&self, receipt: &SandboxReceipt, repo_url: &str, path: &str) -> Result<()> {
let repo_url = validate_git_remote_url(repo_url)?;
let api_key = Self::api_key()?;
let url = Self::toolbox_base(receipt)?.join("git/clone")?;
let body = serde_json::json!({ "url": repo_url, "path": path });
let response = Self::send_json(reqwest::Method::POST, &url, &api_key, body)?;
let status = response.status();
if !status.is_success() {
bail!("Cloud agent repository clone failed (HTTP {status}).");
}
Ok(())
}
fn run_harness(&self, receipt: &SandboxReceipt, command: &HarnessCommand) -> Result<String> {
let api_key = Self::api_key()?;
let url = Self::toolbox_base(receipt)?.join("process/execute")?;
// The toolbox executes one shell command string, so every argv
// element is POSIX-single-quoted — a prompt cannot interpolate.
let body = serde_json::json!({
"command": shell_quote_join(&command.argv),
"cwd": command.cwd,
"timeout": command.timeout_secs,
});
// This call carries the declared turn budget (an hour for the agent
// entry), so it asks for that budget plus slack per request — never the
// 120s control-plane cap that used to bound it.
let response = Self::send_json_on(View on GitHub (pinned to 73e0f67d83)