Skip to content

python.missing_transaction_boundary

Correctness High

Detects database operations that span multiple queries without proper transaction boundaries, risking partial updates and data inconsistency.

Without transaction boundaries:

  • Partial updates — Some changes commit while others fail
  • Data inconsistency — Database left in invalid state
  • Race conditions — Concurrent requests see intermediate states
  • Hard to recover — Manual intervention needed to fix data
# ❌ Before (no transaction boundary)
async def transfer_funds(from_account: str, to_account: str, amount: float):
await db.execute(
"UPDATE accounts SET balance = balance - ? WHERE id = ?",
[amount, from_account]
)
# If this fails, money is lost!
await db.execute(
"UPDATE accounts SET balance = balance + ? WHERE id = ?",
[amount, to_account]
)
# ✅ After (with transaction)
async def transfer_funds(from_account: str, to_account: str, amount: float):
async with db.transaction():
await db.execute(
"UPDATE accounts SET balance = balance - ? WHERE id = ?",
[amount, from_account]
)
await db.execute(
"UPDATE accounts SET balance = balance + ? WHERE id = ?",
[amount, to_account]
)
  • Multiple database writes without transaction wrapper
  • Mixed read/write operations without isolation
  • SQLAlchemy sessions without commit/rollback boundaries
  • Async database operations without transaction context

Unfault generates patches that wrap operations in transactions:

# SQLAlchemy
from sqlalchemy.orm import Session
def create_order(session: Session, order_data: dict):
with session.begin():
order = Order(**order_data)
session.add(order)
for item in order_data['items']:
session.add(OrderItem(order=order, **item))
# Recommended: Use session context manager
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
def process_data():
with Session() as session:
with session.begin():
# All operations here are in one transaction
pass
# Auto-commit on success, rollback on exception