tursodatabase/turso · error · ValueError

: no results found

Error message

{path}: no results found

What it means

After parsing every row of the benchmark CSV, no valid result rows were collected for the file, so there is nothing to plot. The guard fires when the CSV is empty, all rows were rejected by earlier checks (e.g. unsupported engine/mode or profiled rows), or the file only contains header/columns.

Solutions

  1. Point the script at a results CSV that actually contains data rows
  2. Re-run the benchmark to produce results
  3. Check the benchmark output path/arguments if the file is unexpectedly empty
Defensive patterns

Strategy: validation

Validate before calling

import csv
with open(path) as f:
    if sum(1 for _ in csv.DictReader(f)) == 0:
        raise ValueError(f"{path}: no results found")

Type guard

def has_data_rows(path) -> bool:
    with open(path) as f:
        return next(csv.DictReader(f), None) is not None

Try / catch

try:
    configuration, runs = read_runs(path, percentile)
except ValueError as e:
    print(f"no data to plot: {e}")

Prevention

When it happens

Trigger: CSV has a header but zero data rows; a filter or generator wrote only the header; wrong file passed in.

Common situations: Benchmark crashed before writing results; glob picked up an empty template CSV; all rows were removed while cleaning duplicates.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

Thrown at perf/fts/plot/plot-fts.py:117

            if row.get("profiled", "false") != "false":
                raise ValueError("profiled timings must not be used for benchmark comparisons")
            current = tuple(row[key] for key in CONFIGURATION)
            if configuration is None:
                configuration = current
            if configuration != current:
                raise ValueError("plot only one configuration at a time")
            key = (row["engine"], row["mode"])
            if key not in SERIES:
                raise ValueError(f"unsupported engine/mode: {key}")
            series = samples.setdefault(key, {query: [] for query in QUERIES})
            query = row["query"]
            seen = runs.setdefault(row["run"], {}).setdefault(key, set())
            if query not in QUERIES or query in seen:
                raise ValueError(f"{path}: unknown or duplicate query {query}")
            seen.add(query)
            series[query].append(read_measurement(row, percentile))
    if not runs:
        raise ValueError(f"{path}: no results found")
    return configuration, runs


def read_measurement(row, percentile):
    queries = int(row["queries"])
    positive_number(queries)
    if row["benchmark"] != "memory":
        seconds = positive_number(row["seconds"])
    if row["benchmark"] == "memory":
        if row["engine"] != "turso" or queries != int(row["requested_queries"]) * int(row["connections"]):
            raise ValueError("heap results require Turso and the requested queries per connection")
        value = float(row["peak_heap_bytes"]) / (1024 * 1024)
    elif row["benchmark"] == "search":
        if not 0 < int(row["connections"]) <= queries or queries != int(row["requested_queries"]):
            raise ValueError("search results require at least one sample per connection and the requested query count")
        value = float(row[f"{percentile}_ms"])
    elif row["benchmark"] == "throughput":
        minimum = positive_number(row["min_seconds"])

View on GitHub (pinned to 8d4a589f8d)