tursodatabase/turso · error · anyhow::Error
request failed: {status} {text}
Error message
request failed: {status} {text} What it means
Raised by the test-harness HTTP client in bindings/rust/src/sync.rs when a request to the sync server returns a non-success status. handle_response treats HTTP 400 with a body containing 'already exists' as success (idempotent create); every other failing status becomes this error with the status code and full response body embedded.
Source
Thrown at bindings/rust/src/sync.rs:1127
);
assert_eq!(
Builder::new_remote(":memory:")
.with_logical_mvcc_pull(false)
.logical_mvcc_pull,
Some(false)
);
}
async fn handle_response(resp: reqwest::Response) -> Result<()> {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
if status == 400 && text.contains("already exists") {
return Ok(());
}
if !status.is_success() {
return Err(anyhow!("request failed: {status} {text}"));
}
Ok(())
}
pub struct TursoServer {
user_url: String,
db_url: String,
host: String,
db_prefix: String,
server: Option<Child>,
_sync_dir_created_by_harness: Option<TempDir>,
client: Client,
}
impl TursoServer {
pub async fn new() -> Result<Self> {
let client = Client::new();View on GitHub (pinned to 492c4a71cd)
Solutions
- Read the status and body in the message: 404 means wrong path, 400 means payload, 500 means a server-side bug to chase in the server log
- Check the sync server's stdout/stderr for the panic or error that produced the status
- Rebuild both the server binary and the bindings crate from the same commit so routes and payloads match
- For transient 5xx, rerun the test; for 4xx, fix the request path or payload
Example fix
// before
let resp = client.post(url).json(&body).send().await?;
handle_response(resp).await?; // opaque: request failed: 500 ...
// after
let resp = client.post(url).json(&body).send().await?;
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
if !status.is_success() && !(status == 400 && text.contains("already exists")) {
anyhow::bail!("create failed: {status} body: {text}");
} Defensive patterns
Strategy: try-catch
Validate before calling
let resp = client.get(format!("{user_url}/health")).send().await?;
anyhow::ensure!(resp.status().is_success(), "sync server unhealthy before request"); Try / catch
match handle_response(resp).await {
Ok(()) => {}
Err(e) => {
let msg = e.to_string();
if msg.contains("500") || msg.contains("503") { /* retry with backoff */ }
else { return Err(e); }
}
} Prevention
- Build the HTTP client harness and the sync server from the same commit
- Log the response body verbatim on failure - the status alone is not enough
- Treat 400 'already exists' as success for idempotent creates, as the harness does
When it happens
Trigger: POSTing a create/upload request that fails server-side without 'already exists' in the body; hitting a renamed or removed route (404); auth failure (401/403); malformed JSON payload after a sync-protocol schema change (400 without that phrase); server bug returning 500.
Common situations: Client harness and tursodb sync server built from different commits so routes or payload schemas disagree; server crashed mid-test and a proxy returns an error status; typos in the URL path used by a custom harness.
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
- local sync server on port {port} did not become ready within
- local sync server failed to start after {SPAWN_ATTEMPTS} att
- remote sql execution failed: {value}
- invalid response shape
- failed to build IO runtime
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/88ccdbae00076ba2.
Report an issue: GitHub.