tursodatabase/turso · error · anyhow::Error

latency worker panicked

Error message

latency worker panicked

What it means

`measure` spawns one worker thread per session; if any worker panics while executing queries, its JoinHandle yields an Err which `measure` converts to this error. It signals that a benchmark thread crashed mid-run rather than the query itself returning a normal error.

Solutions

  1. Fix the panic inside the `execute` closure (look for unwrap/expect/indexing).
  2. Check panic stderr output for the real panic message and location.
  3. Avoid panicking APIs in `execute`; return `Result::Err` instead so the error propagates normally.

Example fix

// before
let row = rows.get(i).unwrap();
// after
let Some(row) = rows.get(i) else { return Ok(RunResult::default()) };
Defensive patterns

Strategy: try-catch

Try / catch

match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| execute(session, rt))) {
    Ok(res) => res,
    Err(_) => return Err(anyhow::anyhow!("worker panicked; check execute closure for unwrap/index panics")),
}

Prevention

When it happens

Trigger: A worker thread passed to `measure` panics inside the user-supplied `execute` closure (e.g. index-out-of-bounds, unwrap on None, connection closed unexpectedly), making the scoped JoinHandle return an Err.

Common situations: A SQL executor bug or a panic in benchmark code running on a worker thread; a connection being closed or poisoned during the benchmark loop.

Related errors


AI-assisted analysis of tursodatabase/turso@8d4a589f8d (2026-09-20). Data as JSON: /api/errors/97479acf60b744fa. Report an issue: GitHub.

Appendix: source

Thrown at perf/fts/src/latency.rs:68

                        result.rows += completed.rows;
                        result.id_sum += completed.id_sum;
                    }
                    Ok::<_, anyhow::Error>((result, latencies))
                })
            })
            .collect();
        let start = Instant::now();
        barrier.wait();
        let outcomes: Vec<_> = handles.into_iter().map(|handle| handle.join()).collect();
        let seconds = start.elapsed().as_secs_f64();
        let mut measurement = Measurement {
            result: RunResult::default(),
            seconds,
            latencies: Vec::with_capacity(queries),
        };
        for outcome in outcomes {
            let (result, latencies) =
                outcome.map_err(|_| anyhow::anyhow!("latency worker panicked"))??;
            measurement.result.queries += result.queries;
            measurement.result.rows += result.rows;
            measurement.result.id_sum += result.id_sum;
            measurement.latencies.extend(latencies);
        }
        Ok(measurement)
    })
}

#[cfg(test)]
mod tests {
    use std::collections::HashSet;
    use std::sync::Mutex;

    use super::*;

    #[test]
    fn uses_distinct_threads_and_records_each_query_with_uneven_work() -> Result<()> {

View on GitHub (pinned to 8d4a589f8d)