tursodatabase/turso · error

No SQL in batch step

Error message

No SQL in batch step

What it means

Thrown by execute_batch_step when a batch step in a POST /v2/pipeline Batch request has no `sql` on its statement. The wire type makes stmt.sql optional (so steps could theoretically reference prepared statements), but this sync server only executes literal SQL. Unlike most errors in this file it does not become an HTTP 500: execute_batch catches it and returns it inside a 200 response as that step's entry in step_errors with code BATCH_STEP_ERROR.

Source

Thrown at cli/sync_server.rs:431

            }
            BatchCond::And(list) => list
                .conds
                .iter()
                .all(|c| Self::evaluate_condition(c, step_results, step_errors, conn)),
            BatchCond::Or(list) => list
                .conds
                .iter()
                .any(|c| Self::evaluate_condition(c, step_results, step_errors, conn)),
            BatchCond::IsAutocommit {} => conn.get_auto_commit(),
        }
    }

    fn execute_batch_step(&self, conn: &Arc<Connection>, step: &BatchStep) -> Result<StmtResult> {
        let sql = step
            .stmt
            .sql
            .as_ref()
            .ok_or_else(|| anyhow!("No SQL in batch step"))?;

        debug!("Executing batch step SQL: {}", sql);

        let mut stmt = conn.prepare(sql)?;

        for (i, arg) in step.stmt.args.iter().enumerate() {
            let core_value = convert_value_to_core(arg);
            stmt.bind_at(std::num::NonZero::new(i + 1).unwrap(), core_value)?;
        }

        let want_rows = step.stmt.want_rows.unwrap_or(true);

        if want_rows {
            let rows = stmt.run_collect_rows()?;

            let cols: Vec<Col> = (0..stmt.num_columns())
                .map(|i| Col {
                    name: Some(stmt.get_column_name(i).to_string()),

View on GitHub (pinned to bad083fafb)

Solutions

  1. Set a non-empty "sql" string on every step.stmt in the batch request body.
  2. Replace named/stored statement references with inline SQL literals when targeting this server.
  3. Find the failing step by scanning the 200 response's step_errors array for code=BATCH_STEP_ERROR and matching the message.
  4. If stored statements are required, run a server that implements them instead of cli/sync_server.rs.

Example fix

// before
{"requests":[{"type":"batch","batch":{"steps":[{"stmt":{"args":[{"type":"integer","value":1}]}}]}}]}

// after
{"requests":[{"type":"batch","batch":{"steps":[{"stmt":{"sql":"SELECT * FROM t WHERE id = ?","args":[{"type":"integer","value":1}]}}]}}]}
Defensive patterns

Strategy: validation

Validate before calling

fn validate_batch_steps(req: &BatchStreamReq) -> Result<(), String> {
    for (i, step) in req.batch.steps.iter().enumerate() {
        let has_sql = step.stmt.sql.as_deref().is_some_and(|s| !s.trim().is_empty());
        if !has_sql {
            return Err(format!("batch step {i} has no sql"));
        }
    }
    Ok(()
)}

Type guard

function stepHasSql(step: BatchStep | undefined): step is BatchStep & { stmt: { sql: string } } {
  return typeof step?.stmt?.sql === "string" && step.stmt.sql.trim().length > 0;
}

Try / catch

The endpoint returns HTTP 200 even on step failure: after each batch, iterate step_errors and treat any entry with code=BATCH_STEP_ERROR (message 'No SQL in batch step') as a failed step, mapping its array index back to the request step that lacks sql.

Prevention

When it happens

Trigger: POST /v2/pipeline with StreamRequest::Batch where a step's `stmt` object omits `sql` or sends only `args`/`want_rows`; or a client written for the sqld/Hrana v2 API that sends named/stored statements, which cli/sync_server.rs does not implement.

Common situations: Hand-written JSON pipeline payloads during debugging; porting a client from the production sqld HTTP API that relies on named statements; serde deserializers that default the sql field to None.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/060a796f219161cb. Report an issue: GitHub.