Fix UNIQUE constraint error in get_or_create_user_account

Handles race condition where user account already exists from initial sync
but without user_id set. When user configures wallet, code now:
- Catches IntegrityError on UNIQUE constraint for accounts.name
- Fetches existing account by name
- Updates user_id if NULL or different
- Returns existing account instead of failing

This fixes the error that occurred when users configured their wallet after
their accounts were created during the initial Beancount sync.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
padreug 2025-11-11 19:47:17 +01:00
parent a71d9b7fa5
commit 72e8fe8ee4

39
crud.py
View file

@ -283,6 +283,7 @@ async def get_or_create_user_account(
logger.error(f"[CASTLE DB] Account still not found after sync: {account_name}")
# Fallback: create directly in Castle DB if sync failed
logger.info(f"[CASTLE DB] Creating account directly in Castle DB: {account_name}")
try:
account = await create_account(
CreateAccount(
name=account_name,
@ -291,6 +292,44 @@ async def get_or_create_user_account(
user_id=user_id,
)
)
except Exception as e:
# Handle UNIQUE constraint error - account already exists
if "UNIQUE constraint failed" in str(e) and "accounts.name" in str(e):
logger.warning(f"[CASTLE DB] Account already exists (UNIQUE constraint), fetching by name: {account_name}")
# Fetch existing account by name only (ignore user_id in query)
account = await db.fetchone(
"""
SELECT * FROM accounts
WHERE name = :name
""",
{"name": account_name},
Account,
)
if account:
logger.info(f"[CASTLE DB] Found existing account: {account_name} (user_id: {account.user_id})")
# Update user_id if it's NULL or different
if account.user_id != user_id:
logger.info(f"[CASTLE DB] Updating account user_id from {account.user_id} to {user_id}")
await db.execute(
"""
UPDATE accounts
SET user_id = :user_id
WHERE name = :name
""",
{"user_id": user_id, "name": account_name}
)
# Refresh account from DB
account = await db.fetchone(
"""
SELECT * FROM accounts
WHERE name = :name
""",
{"name": account_name},
Account,
)
else:
# Re-raise if it's a different error
raise
else:
logger.info(f"[CASTLE DB] Account already exists in Castle DB: {account_name}")