{"record":{"id":"876b183c57a02260","repo":"tursodatabase/turso","slug":"plot-only-one-configuration-at-a-time","errorCode":null,"errorMessage":"plot only one configuration at a time","messagePattern":"plot only one configuration at a time","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"perf/fts/plot/plot-fts.py","lineNumber":77,"sourceCode":"            configuration = current\n        if current != configuration or (points and set(samples) != set(next(iter(points.values())))):\n            raise ValueError(\"sweep files must match benchmark, sample budget, fixture and engine/mode series\")\n        points[connections] = samples\n    if not points:\n        raise ValueError(\"no sweep results found\")\n    return configuration, dict(sorted(points.items()))\n\n\ndef read_runs(paths, percentile=\"p50\"):\n    configuration = None\n    samples = {}\n    expected_series = None\n    for path in paths:\n        current, runs = read_file(path, percentile, samples)\n        if configuration is None:\n            configuration = current\n        if configuration != current:\n            raise ValueError(\"plot only one configuration at a time\")\n        for run in runs.values():\n            if expected_series is None:\n                expected_series = set(run)\n            if set(run) != expected_series or any(seen != set(QUERIES) for seen in run.values()):\n                raise ValueError(f\"{path}: each run must contain the same series and all six query cases\")\n    if configuration is None:\n        raise ValueError(\"no results found\")\n    configuration = dict(zip(CONFIGURATION, configuration))\n    configuration[\"percentile\"] = percentile\n    return configuration, samples\n\n\ndef read_file(path, percentile, samples):\n    configuration = None\n    runs = {}\n    with path.open(newline=\"\") as stream:\n        reader = csv.DictReader(stream)\n        required = set(CONFIGURATION) | {\"engine\", \"mode\", \"run\", \"query\", \"queries\"}","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/tursodatabase/turso/blob/8d4a589f8d13ac184700d2a8f724f27e1995be3b/perf/fts/plot/plot-fts.py#L59-L95","documentation":"read_runs groups benchmark runs for plotting and requires all input CSVs to share one configuration tuple (the CONFIGURATION columns). It raises this ValueError when a file's configuration differs from the first file's, because the plotter renders a single configuration per chart.","triggerScenarios":"Calling read_runs (from main or read_sweep) with CSV files whose CONFIGURATION column values differ between files.","commonSituations":"Combining benchmark outputs from different sample budgets, percentiles, or fixture settings; plotting files from two separate benchmark campaigns on one command line.","solutions":["Plot one configuration per invocation: pass only CSVs sharing identical CONFIGURATION columns.","Split the invocation into multiple plot runs, one per configuration.","Regenerate the mismatched CSVs with the intended configuration."],"exampleFix":"# before\npython plot-fts.py budget-1000/results.csv budget-5000/results.csv\n# after\npython plot-fts.py budget-1000/results.csv","handlingStrategy":"validation","validationCode":"import csv\ndef config_of(p):\n    r = csv.DictReader(open(p))\n    return tuple(next(r)[k] for k in CONFIGURATION)\nassert len({config_of(p) for p in paths}) == 1, 'pass only files from one configuration per plot'","typeGuard":null,"tryCatchPattern":"try:\n    config, samples = read_runs(paths)\nexcept ValueError as e:\n    if 'only one configuration' in str(e):\n        print('Fix: split the plot into one invocation per configuration')\n    else:\n        raise","preventionTips":["Group result files by configuration in separate directories and plot per directory.","Do not mix CSVs from different benchmark campaigns in one plot-fts.py call.","Record the configuration tuple in the filename to spot mismatches early."],"tags":["python","benchmarking","input-validation"],"backgroundTag":"conflicting-config-options","analyzedSha":"8d4a589f8d13ac184700d2a8f724f27e1995be3b","analyzedAt":"2026-09-20T13:18:14.658Z","contentChangedAt":"2026-09-20T13:18:14.658Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}