tursodatabase/turso · error
Failed to parse pipeline request: {}
Error message
Failed to parse pipeline request: {} What it means
The test sync server's POST /v2/pipeline endpoint parses the request body with serde_json into PipelineReqBody. This error means the body is not valid JSON for that schema; the serde message is appended and names the offending field. PipelineReqBody expects an object with a requests array whose entries are Execute or Batch stream requests.
Source
Thrown at cli/sync_server.rs:303
error!("Request error: {}", e);
HttpResponse {
status: 500,
content_type: "text/plain".to_string(),
body: format!("Internal Server Error: {e}").into_bytes(),
}
}
};
let response_bytes = format_http_response(&http_response);
stream.write_all(&response_bytes)?;
stream.flush()?;
Ok(())
}
fn handle_pipeline(&self, db: &DbHandle, body: &[u8]) -> Result<HttpResponse> {
let req: PipelineReqBody = serde_json::from_slice(body)
.map_err(|e| anyhow!("Failed to parse pipeline request: {}", e))?;
debug!("Pipeline request: {:?}", req);
let conn = db.conn.lock().unwrap();
let mut results = Vec::new();
for request in req.requests {
let result = match request {
StreamRequest::Execute(exec_req) => self.execute_statement(&conn, &exec_req),
StreamRequest::Batch(batch_req) => self.execute_batch(&conn, &batch_req),
StreamRequest::None => StreamResult::Error {
error: Error {
message: "Unknown request type".to_string(),
code: "UNKNOWN".to_string(),
},
},
};View on GitHub (pinned to 492c4a71cd)
Solutions
- Log the exact request body and compare it field-by-field against the PipelineReqBody / StreamRequest serde types in this server build
- Fix the payload: top-level requests array; each entry is an Execute or Batch object with the expected fields
- Rebuild client and server from the same revision so the generated JSON shapes match
- Send the body produced by the client library's serializer instead of constructing JSON by hand
Example fix
// before
curl -d '{"request": [{"stmt": {"sql": "SELECT 1"}}]}' localhost:8080/v2/pipeline
// after
curl -d '{"requests": [{"Execute": {"stmt": {"sql": "SELECT 1"}}}]}' localhost:8080/v2/pipeline Defensive patterns
Strategy: validation
Validate before calling
function validatePipelineRequest(body) {
const parsed = JSON.parse(body); // throws early on bad JSON
if (!parsed || !Array.isArray(parsed.requests)) {
throw new Error('requests must be an array');
}
for (const r of parsed.requests) {
if (!(('Execute' in r) || ('Batch' in r))) {
throw new Error('each entry must be Execute or Batch');
}
}
return parsed;
} Type guard
function isPipelineRequest(v: unknown): v is { requests: unknown[] } {
return typeof v === 'object' && v !== null && Array.isArray((v as any).requests);
} Try / catch
// server/operator side: surface the serde message with a 400-style response
match self.handle_pipeline(&body) {
Ok(resp) => resp,
Err(e) if e.to_string().starts_with("Failed to parse pipeline request") => {
HttpResponse { status: 400, content_type: "text/plain".into(), body: e.to_string().into_bytes() }
}
Err(e) => /* 500 */,
} Prevention
- Build client and server from the same commit so the JSON contract cannot drift
- Always serialize requests with the client library, never hand-build JSON
- Add a contract test that posts a serialized PipelineReqBody and asserts 200
When it happens
Trigger: POSTing to /v2/pipeline with syntactically invalid JSON, a missing or non-array requests field, entries whose shape matches neither StreamRequest::Execute nor StreamRequest::Batch, or wrong field types (e.g. args not an array).
Common situations: Hand-written curl payloads during debugging; client and server built from different commits so the PipelineReqBody JSON contract drifted; a proxy truncating or rewriting bodies; sending the protobuf /pull-updates payload to the JSON pipeline route by mistake.
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
- Remote request returned an empty response.
- Extensions array cannot be null.
- Extensions must be "all" or an array of extension names.
- Only finite numbers (not Infinity or NaN) can be passed as a
- Unexpected token for last_insert_rowid: {reader.TokenType}
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-09-13).
Data as JSON: /api/errors/a670a1ee791bced6.
Report an issue: GitHub.