{"record":{"id":"f361e637be8ea6ef","repo":"tursodatabase/turso","slug":"failed-to-decode-pullupdatesrequest","errorCode":null,"errorMessage":"Failed to decode PullUpdatesRequest: {}","messagePattern":"Failed to decode PullUpdatesRequest: (.+?)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"cli/sync_server.rs","lineNumber":488,"sourceCode":"            })\n        } else {\n            stmt.run_ignore_rows()?;\n            Ok(StmtResult {\n                cols: vec![],\n                rows: vec![],\n                affected_row_count: 0,\n                last_insert_rowid: None,\n                replication_index: None,\n                rows_read: 0,\n                rows_written: 0,\n                query_duration_ms: 0.0,\n            })\n        }\n    }\n\n    fn handle_pull_updates(&self, body: &[u8]) -> Result<HttpResponse> {\n        let req = <PullUpdatesReqProtoBody as Message>::decode(body)\n            .map_err(|e| anyhow!(\"Failed to decode PullUpdatesRequest: {}\", e))?;\n\n        debug!(\n            \"Pull updates request: server_revision={}, client_revision={}\",\n            req.server_revision, req.client_revision\n        );\n\n        let encoding =\n            PageUpdatesEncodingReq::try_from(req.encoding).unwrap_or(PageUpdatesEncodingReq::Raw);\n\n        if encoding == PageUpdatesEncodingReq::Zstd {\n            return Err(anyhow!(\"Zstd encoding is not supported\"));\n        }\n\n        if PullUpdatesStreamKind::try_from(req.stream_kind).unwrap_or(PullUpdatesStreamKind::Pages)\n            == PullUpdatesStreamKind::MvccLogicalLog\n        {\n            return self.handle_logical_pull_updates(&req);\n        }","sourceCodeStart":470,"sourceCodeEnd":506,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/cli/sync_server.rs#L470-L506","documentation":"/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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: JSON body\nlet resp = post(url, serde_json::to_vec(&req)?).await?;\n\n// after: raw protobuf body\nuse prost::Message;\nlet resp = post(url, PullUpdatesReqProtoBody::encode_to_vec(&req)).await?;","handlingStrategy":"validation","validationCode":"use prost::Message;\nlet body = PullUpdatesReqProtoBody::encode_to_vec(&req);\n// round-trip check: if this fails locally, the server will fail too\nPullUpdatesReqProtoBody::decode(body.as_slice())\n    .map_err(|e| format!(\"request body is not a decodable PullUpdatesRequest: {e}\"))?;","typeGuard":null,"tryCatchPattern":"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.","preventionTips":["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."],"tags":["protobuf","sync","pull-updates","serialization","http"],"backgroundTag":"protobuf-decode-failed","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}