tursodatabase/turso · error
Failed to decode PullUpdatesRequest: {}
Error message
Failed to decode PullUpdatesRequest: {} What it means
/pull-updates expects the raw HTTP body to be a prost-encoded PullUpdatesReqProtoBody protobuf message. This error means prost's Message::decode rejected the bytes, so what arrived is not a valid protobuf message of that type; the connection handler turns it into an HTTP 500 with 'Internal Server Error: Failed to decode PullUpdatesRequest: ...'. Typical causes are a non-protobuf body, truncation, or field tag/type drift between client and server proto definitions.
Source
Thrown at cli/sync_server.rs:488
})
} else {
stmt.run_ignore_rows()?;
Ok(StmtResult {
cols: vec![],
rows: vec![],
affected_row_count: 0,
last_insert_rowid: None,
replication_index: None,
rows_read: 0,
rows_written: 0,
query_duration_ms: 0.0,
})
}
}
fn handle_pull_updates(&self, body: &[u8]) -> Result<HttpResponse> {
let req = <PullUpdatesReqProtoBody as Message>::decode(body)
.map_err(|e| anyhow!("Failed to decode PullUpdatesRequest: {}", e))?;
debug!(
"Pull updates request: server_revision={}, client_revision={}",
req.server_revision, req.client_revision
);
let encoding =
PageUpdatesEncodingReq::try_from(req.encoding).unwrap_or(PageUpdatesEncodingReq::Raw);
if encoding == PageUpdatesEncodingReq::Zstd {
return Err(anyhow!("Zstd encoding is not supported"));
}
if PullUpdatesStreamKind::try_from(req.stream_kind).unwrap_or(PullUpdatesStreamKind::Pages)
== PullUpdatesStreamKind::MvccLogicalLog
{
return self.handle_logical_pull_updates(&req);
}View on GitHub (pinned to bad083fafb)
Solutions
- Send exactly the bytes produced by PullUpdatesReqProtoBody::encode_to_vec() as the request body.
- Verify the HTTP Content-Length matches the encoded body length and that no proxy modifies the payload.
- Rebuild client and server from the same workspace commit so the protobuf definitions match.
- If every request fails decode, log body length and content-type: a consistent failure means wrong wire format, not corruption.
Example fix
// before: JSON body let resp = post(url, serde_json::to_vec(&req)?).await?; // after: raw protobuf body use prost::Message; let resp = post(url, PullUpdatesReqProtoBody::encode_to_vec(&req)).await?;
Defensive patterns
Strategy: validation
Validate before calling
use prost::Message;
let body = PullUpdatesReqProtoBody::encode_to_vec(&req);
// round-trip check: if this fails locally, the server will fail too
PullUpdatesReqProtoBody::decode(body.as_slice())
.map_err(|e| format!("request body is not a decodable PullUpdatesRequest: {e}"))?; Try / catch
On HTTP 500 containing 'Failed to decode PullUpdatesRequest', do not retry the same bytes: log body length and content-type, re-encode the request from the proto type, and retry once.
Prevention
- Send prost/protobuf bytes to /pull-updates, never JSON.
- Derive Content-Length from the encoded buffer length; avoid proxies that rewrite bodies.
- Generate client and server from the same server_proto.rs revision to prevent field tag drift.
When it happens
Trigger: POST /pull-updates with a JSON or text body; a body truncated by a wrong Content-Length or a rewriting proxy; a client compiled against a different revision of sync/engine/src/server_proto.rs whose field tags/types disagree with the server's.
Common situations: Testing the endpoint with curl before a real client exists; version skew between client SDK and server after proto schema changes; middleware that re-encodes, compresses, or truncates request bodies.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- HTTP request failed: ${e instanceof Error ? e.message : Stri
- Zstd encoding is not supported
- Failed to parse server_pages_selector: {}
- No active remote transaction
- reader is null
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/f361e637be8ea6ef.
Report an issue: GitHub.