Return existing asset instead of error when asset already exists

When publishing to L2, if the asset already exists, return it with
existing: true flag instead of raising a 400 error. This makes
re-publishing idempotent and handles race conditions gracefully.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-09 12:35:01 +00:00
parent b810a65f5b
commit f9b7f784e5

View File

@@ -1784,9 +1784,11 @@ async def _register_asset_impl(req: RegisterRequest, owner: str):
if not req.ipfs_cid:
raise HTTPException(400, "IPFS CID is required for registration")
# Check if name exists
if await db.asset_exists(req.name):
raise HTTPException(400, f"Asset already exists: {req.name}")
# Check if name exists - return existing asset if so
existing = await db.get_asset_by_name(req.name)
if existing:
logger.info(f"register_asset: Asset {req.name} already exists, returning existing")
return {"asset": existing, "activity": None, "existing": True}
# ===== PHASE 2: IPFS OPERATIONS (non-blocking) =====
import asyncio
@@ -1805,7 +1807,10 @@ async def _register_asset_impl(req: RegisterRequest, owner: str):
async with db.transaction() as conn:
# Check name again inside transaction (race condition protection)
if await db.asset_exists_by_name_tx(conn, req.name):
raise HTTPException(400, f"Asset already exists: {req.name}")
# Race condition - another request created it first, return existing
existing = await db.get_asset_by_name(req.name)
logger.info(f"register_asset: Asset {req.name} created by concurrent request")
return {"asset": existing, "activity": None, "existing": True}
# Create asset
asset = {
@@ -2084,9 +2089,11 @@ async def record_run(req: RecordRunRequest, user: User = Depends(get_required_us
}
await db.create_asset_tx(conn, input_asset)
# Check output doesn't already exist (by content_hash)
# Check if output already exists (by content_hash) - return existing if so
if await db.asset_exists_by_name_tx(conn, output_hash):
raise HTTPException(400, f"Asset already exists: {output_hash[:16]}...")
existing = await db.get_asset_by_name(output_hash)
logger.info(f"record_run: Output {output_hash[:16]}... already exists")
return {"asset": existing, "activity": None, "existing": True}
# Create output asset with provenance - named by content_hash
output_asset = {