From 48b5287bafed580d20f3f42a2aee54168870289a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Sun, 15 Mar 2026 14:32:33 +0100 Subject: [PATCH] fix: rollback DB session after failed tool execution in chat agent When a tool like query_database fails (e.g., bad column name), the SQLAlchemy session enters a failed transaction state. Subsequent operations (like saving the assistant response) then also fail with InFailedSQLTransactionError. Fix: rollback the session in the except block of _execute_tool(). Also improved query_database tool description with correct column names (category_key not category) to help the AI write valid SQL. Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/app/services/chat_service.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/backend/app/services/chat_service.py b/backend/app/services/chat_service.py index 6aa33af..7ddf520 100644 --- a/backend/app/services/chat_service.py +++ b/backend/app/services/chat_service.py @@ -219,7 +219,7 @@ TOOLS = [ "type": "function", "function": { "name": "query_database", - "description": "Execute a read-only SQL SELECT query against the database. Results are automatically filtered to the current tenant. Tables: orders, order_lines, products, cad_files, materials, material_aliases, output_types, media_assets, render_templates.", + "description": "Execute a read-only SQL SELECT query against the database. Key columns: products(id, name, pim_id, category_key, cad_file_id, is_active), orders(id, order_number, status, tenant_id), order_lines(id, order_id, product_id, render_status, material_override, render_overrides). Use :tenant_id parameter for tenant filtering. Category is 'category_key' not 'category'.", "parameters": { "type": "object", "properties": { @@ -292,8 +292,13 @@ async def _execute_tool( else: return json.dumps({"error": f"Unknown tool: {name}"}) except Exception as exc: - logger.exception("Tool execution failed: %s(%s)", name, arguments) - return json.dumps({"error": str(exc)}) + logger.warning("Tool execution failed: %s(%s): %s", name, arguments, exc) + # Rollback the DB session so subsequent operations still work + try: + await db.rollback() + except Exception: + pass + return json.dumps({"error": str(exc)[:500]}) async def _tool_list_orders(db: AsyncSession, tenant_id: str, status: str = "", limit: int = 10) -> str: