This is an automated email from the ASF dual-hosted git repository.

skrawcz pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/burr.git


The following commit(s) were added to refs/heads/main by this push:
     new 692789e2 fix(persistence): improve error message for uninitialized 
SQLitePersister (#613)
692789e2 is described below

commit 692789e21e2c950bd97c201d42356f8748affce3
Author: lif <[email protected]>
AuthorDate: Thu Jan 1 04:47:48 2026 +0800

    fix(persistence): improve error message for uninitialized SQLitePersister 
(#613)
    
    * fix(persistence): improve error message for uninitialized SQLitePersister
    
    When load(), save(), or list_app_ids() are called before initialize(),
    raise a clear RuntimeError with actionable guidance instead of cryptic
    sqlite3.OperationalError about missing table.
    
    Closes #417
    
    Signed-off-by: majiayu000 <[email protected]>
    
    * refactor: extract error message to constant
    
    Address review feedback: extract the uninitialized persister
    error message to a module-level constant _UNINITIALIZED_PERSISTER_ERROR
    and use it in all three places (list_app_ids, load, save).
    
    ---------
    
    Signed-off-by: majiayu000 <[email protected]>
---
 burr/core/persistence.py       | 92 +++++++++++++++++++++++++++---------------
 tests/core/test_persistence.py | 32 +++++++++++++++
 2 files changed, 92 insertions(+), 32 deletions(-)

diff --git a/burr/core/persistence.py b/burr/core/persistence.py
index f217f739..c32bf8e9 100644
--- a/burr/core/persistence.py
+++ b/burr/core/persistence.py
@@ -33,6 +33,13 @@ try:
 except ImportError:
     Self = None
 
+# Error message template for uninitialized SQLitePersister
+_UNINITIALIZED_PERSISTER_ERROR = (
+    "Uninitialized persister: table '{table_name}' does not exist. "
+    "Make sure to call .initialize() on the persister before passing it "
+    "to the ApplicationBuilder."
+)
+
 
 class PersistedStateData(TypedDict):
     partition_key: str
@@ -444,12 +451,19 @@ class SQLitePersister(BaseStatePersister, BaseCopyable):
         )
 
         cursor = self.connection.cursor()
-        cursor.execute(
-            f"SELECT DISTINCT app_id FROM {self.table_name} "
-            f"WHERE partition_key = ? "
-            f"ORDER BY created_at DESC",
-            (partition_key,),
-        )
+        try:
+            cursor.execute(
+                f"SELECT DISTINCT app_id FROM {self.table_name} "
+                f"WHERE partition_key = ? "
+                f"ORDER BY created_at DESC",
+                (partition_key,),
+            )
+        except sqlite3.OperationalError as e:
+            if "no such table" in str(e):
+                raise RuntimeError(
+                    
_UNINITIALIZED_PERSISTER_ERROR.format(table_name=self.table_name)
+                ) from e
+            raise
         app_ids = [row[0] for row in cursor.fetchall()]
         return app_ids
 
@@ -475,27 +489,34 @@ class SQLitePersister(BaseStatePersister, BaseCopyable):
         )
         logger.debug("Loading %s, %s, %s", partition_key, app_id, sequence_id)
         cursor = self.connection.cursor()
-        if app_id is None:
-            # get latest for all app_ids
-            cursor.execute(
-                f"SELECT position, state, sequence_id, app_id, created_at, 
status FROM {self.table_name} "
-                f"WHERE partition_key = ? "
-                f"ORDER BY CREATED_AT DESC LIMIT 1",
-                (partition_key,),
-            )
-        elif sequence_id is None:
-            cursor.execute(
-                f"SELECT position, state, sequence_id, app_id, created_at, 
status FROM {self.table_name} "
-                f"WHERE partition_key = ? AND app_id = ? "
-                f"ORDER BY sequence_id DESC LIMIT 1",
-                (partition_key, app_id),
-            )
-        else:
-            cursor.execute(
-                f"SELECT position, state, sequence_id, app_id, created_at, 
status FROM {self.table_name} "
-                f"WHERE partition_key = ? AND app_id = ? AND sequence_id = ?",
-                (partition_key, app_id, sequence_id),
-            )
+        try:
+            if app_id is None:
+                # get latest for all app_ids
+                cursor.execute(
+                    f"SELECT position, state, sequence_id, app_id, created_at, 
status FROM {self.table_name} "
+                    f"WHERE partition_key = ? "
+                    f"ORDER BY CREATED_AT DESC LIMIT 1",
+                    (partition_key,),
+                )
+            elif sequence_id is None:
+                cursor.execute(
+                    f"SELECT position, state, sequence_id, app_id, created_at, 
status FROM {self.table_name} "
+                    f"WHERE partition_key = ? AND app_id = ? "
+                    f"ORDER BY sequence_id DESC LIMIT 1",
+                    (partition_key, app_id),
+                )
+            else:
+                cursor.execute(
+                    f"SELECT position, state, sequence_id, app_id, created_at, 
status FROM {self.table_name} "
+                    f"WHERE partition_key = ? AND app_id = ? AND sequence_id = 
?",
+                    (partition_key, app_id, sequence_id),
+                )
+        except sqlite3.OperationalError as e:
+            if "no such table" in str(e):
+                raise RuntimeError(
+                    
_UNINITIALIZED_PERSISTER_ERROR.format(table_name=self.table_name)
+                ) from e
+            raise
         row = cursor.fetchone()
         if row is None:
             return None
@@ -551,11 +572,18 @@ class SQLitePersister(BaseStatePersister, BaseCopyable):
         )
         cursor = self.connection.cursor()
         json_state = json.dumps(state.serialize(**self.serde_kwargs))
-        cursor.execute(
-            f"INSERT INTO {self.table_name} (partition_key, app_id, 
sequence_id, position, state, status) "
-            f"VALUES (?, ?, ?, ?, ?, ?)",
-            (partition_key, app_id, sequence_id, position, json_state, status),
-        )
+        try:
+            cursor.execute(
+                f"INSERT INTO {self.table_name} (partition_key, app_id, 
sequence_id, position, state, status) "
+                f"VALUES (?, ?, ?, ?, ?, ?)",
+                (partition_key, app_id, sequence_id, position, json_state, 
status),
+            )
+        except sqlite3.OperationalError as e:
+            if "no such table" in str(e):
+                raise RuntimeError(
+                    
_UNINITIALIZED_PERSISTER_ERROR.format(table_name=self.table_name)
+                ) from e
+            raise
         self.connection.commit()
 
     def cleanup(self):
diff --git a/tests/core/test_persistence.py b/tests/core/test_persistence.py
index b990b7d6..b362cd96 100644
--- a/tests/core/test_persistence.py
+++ b/tests/core/test_persistence.py
@@ -94,6 +94,38 @@ def 
test_sqlite_persistence_is_initialized_true_new_connection(tmp_path):
     p2.cleanup()
 
 
+def test_sqlite_persister_load_without_initialize_raises_runtime_error():
+    """Test that calling load() without initialize() raises a clear 
RuntimeError."""
+    persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
+    try:
+        with pytest.raises(RuntimeError, match="Uninitialized persister"):
+            persister.load("partition_key", "app_id")
+    finally:
+        persister.cleanup()
+
+
+def test_sqlite_persister_save_without_initialize_raises_runtime_error():
+    """Test that calling save() without initialize() raises a clear 
RuntimeError."""
+    persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
+    try:
+        with pytest.raises(RuntimeError, match="Uninitialized persister"):
+            persister.save(
+                "partition_key", "app_id", 1, "position", State({"key": 
"value"}), "completed"
+            )
+    finally:
+        persister.cleanup()
+
+
+def 
test_sqlite_persister_list_app_ids_without_initialize_raises_runtime_error():
+    """Test that calling list_app_ids() without initialize() raises a clear 
RuntimeError."""
+    persister = SQLLitePersister(db_path=":memory:", table_name="test_table")
+    try:
+        with pytest.raises(RuntimeError, match="Uninitialized persister"):
+            persister.list_app_ids("partition_key")
+    finally:
+        persister.cleanup()
+
+
 @pytest.mark.parametrize(
     "method_name,kwargs",
     [

Reply via email to