tursodatabase/turso · error

query {} did not return a JSON plan

Error message

query {} did not return a JSON plan

What it means

print_plan runs `EXPLAIN QUERY PLAN FORMAT=JSON <query>` and expects the first cell of the first row to be a Text value containing the JSON plan. If there are no rows, the row is empty, or the value is not Text (e.g. NULL or a blob), it fails with this error naming the query.

Source

Thrown at perf/join-benchmark/main.rs:182

fn execute(database: &Database, statement: &mut turso_core::Statement) -> Result<u64> {
    let mut result_rows = 0_u64;
    loop {
        match statement.step()? {
            StepResult::Row => result_rows = result_rows.saturating_add(1),
            StepResult::IO | StepResult::Yield | StepResult::Sleep { .. } => database.io.step()?,
            StepResult::Done => return Ok(result_rows),
            StepResult::Interrupt => bail!("query was interrupted"),
            StepResult::Busy => bail!("database was busy"),
        }
    }
}

fn print_plan(connection: &Arc<turso_core::Connection>, query: &Query) -> Result<()> {
    let sql = format!("EXPLAIN QUERY PLAN FORMAT=JSON {}", query.sql);
    let rows = connection.prepare(sql)?.run_collect_rows()?;
    let Some(Value::Text(plan)) = rows.first().and_then(|row| row.first()) else {
        bail!("query {} did not return a JSON plan", query.name);
    };
    let plan: JsonValue = serde_json::from_str(plan.as_str())?;
    println!("{}", json!({"query": query.name, "plan": plan}));
    Ok(())
}

View on GitHub (pinned to 492c4a71cd)

Solutions

  1. Run the query manually in tursodb: `EXPLAIN QUERY PLAN FORMAT=JSON <sql>;` to see what it returns.
  2. Fix the query SQL (missing/misspelled tables) so a plan is produced.
  3. Confirm the engine build supports EXPLAIN QUERY PLAN FORMAT=JSON; upgrade or drop the FORMAT clause.
  4. Check run_collect_rows results for errors swallowed upstream before the pattern match.

Example fix

// before
let sql = format!("EXPLAIN QUERY PLAN FORMAT=JSON {}", query.sql);
// after
// verify the statement first
connection.prepare(query.sql.clone())?.run_collect_rows()?;
let sql = format!("EXPLAIN QUERY PLAN FORMAT=JSON {}", query.sql);
Defensive patterns

Strategy: try-catch

Try / catch

let rows = connection.prepare(sql)?.run_collect_rows()?;
let plan_text = rows.first().and_then(|r| r.first())
    .and_then(|v| if let Value::Text(t) = v { Some(t.clone()) } else { None });
match plan_text {
    Some(t) => println!("{}", t),
    None => eprintln!("skipping plan for {} (no JSON plan returned)", query.name),
}

Prevention

When it happens

Trigger: Calling print_plan for a query whose EXPLAIN QUERY PLAN FORMAT=JSON produced no output rows or a non-text first column — e.g. the query references a missing table so planning yields no plan, or the FORMAT=JSON option isn't supported so output shape differs.

Common situations: Query SQL referring to a table that doesn't exist in the benchmark database; older engine build without FORMAT=JSON support; prepare/run failing silently and returning an empty row set.

Related errors


AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-09-13). Data as JSON: /api/errors/ba4e21246440b2bb. Report an issue: GitHub.