From 33cab5e4ff85e5cb2c16cdf06b55032cd4c56505 Mon Sep 17 00:00:00 2001 From: Charlie Tonneslan Date: Sun, 17 May 2026 19:54:45 -0400 Subject: [PATCH] insert: don't crash on header-only CSV with --detect-types When the input has a header but no data rows, the insert path never creates the table - the row loop has nothing to feed into the first insert. The post-insert tracker.transform call then asserts the table exists and blows up: AssertionError: Cannot transform a table that doesn't exist yet Reported in #702 with the exact fix in the body. Guard the transform on db[table].exists() so an empty CSV is just a no-op, matching what --detect-types already does for a zero-byte CSV (no header either). Closes #702. Signed-off-by: Charlie Tonneslan --- sqlite_utils/cli.py | 5 ++++- tests/test_cli.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index 5844dfc0..fe020f9c 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -1175,7 +1175,10 @@ def insert_upsert_implementation( ) else: raise - if tracker is not None: + # An empty CSV (header row but no data rows) never creates the table, + # so there's nothing to transform. Without this guard, --detect-types + # crashes on header-only input (see #702). + if tracker is not None and db[table].exists(): db.table(table).transform(types=tracker.types) # Clean up open file-like objects diff --git a/tests/test_cli.py b/tests/test_cli.py index 40b36854..5f8e5e77 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2318,6 +2318,23 @@ def test_upsert_detect_types(tmpdir, option): ] +@pytest.mark.parametrize("option", ("-d", "--detect-types")) +def test_insert_csv_header_only_with_detect_types(tmpdir, option): + """Header-only CSVs (no data rows) used to crash --detect-types because + the transform step ran against a table that was never created. Now we + skip transform when there's nothing to transform. (#702)""" + db_path = str(tmpdir / "test.db") + result = CliRunner().invoke( + cli.cli, + ["insert", db_path, "empty", "-", "--csv", option], + catch_exceptions=False, + input="name,age,weight\n", + ) + assert result.exit_code == 0 + db = Database(db_path) + assert "empty" not in db.table_names() + + def test_csv_detect_types_creates_real_columns(tmpdir): """Test that CSV import creates REAL columns for floats (default behavior)""" db_path = str(tmpdir / "test.db")