fix: narrow exception handling and support migrate_to for broken credentials

- Catch only ValueError (decryption errors) instead of broad Exception
  so NotFoundError and other failures propagate correctly
- Support migrate_to parameter in the fallback delete path so linked
  models can be reassigned instead of always cascade-deleted
- Sanitize decryption_error message to not expose raw exception details
This commit is contained in:
Luis Novo 2026-04-14 10:34:32 -03:00
parent ba01f7df4e
commit 0c2522074d
2 changed files with 24 additions and 8 deletions

View file

@ -269,7 +269,7 @@ async def delete_credential(
try:
try:
cred = await Credential.get(credential_id)
except Exception as decrypt_err:
except ValueError as decrypt_err:
# Credential exists but can't be decrypted (wrong encryption key).
# Fall back to direct DB operations for deletion.
logger.warning(
@ -277,17 +277,33 @@ async def delete_credential(
f"falling back to direct delete: {decrypt_err}"
)
# Delete linked models directly
# Query linked models
linked = await repo_query(
"SELECT * FROM model WHERE credential = $cred_id",
{"cred_id": ensure_record_id(credential_id)},
)
deleted_models = 0
for model_row in linked:
model_id = str(model_row.get("id", ""))
if model_id:
await repo_delete(model_id)
deleted_models += 1
if linked and migrate_to:
# Migrate models to another credential
target_cred = await Credential.get(migrate_to)
for model_row in linked:
model_id = str(model_row.get("id", ""))
if model_id:
await repo_query(
"UPDATE $model_id SET credential = $target_id",
{
"model_id": ensure_record_id(model_id),
"target_id": ensure_record_id(target_cred.id),
},
)
elif linked:
# Cascade-delete linked models
for model_row in linked:
model_id = str(model_row.get("id", ""))
if model_id:
await repo_delete(model_id)
deleted_models += 1
# Delete the credential itself
await repo_delete(credential_id)

View file

@ -152,7 +152,7 @@ class Credential(ObjectModel):
name=row.get("name", "Unknown"),
provider=row.get("provider", "unknown"),
modalities=row.get("modalities", []),
decryption_error=f"Failed to decrypt API key. The encryption key may have changed. Error: {e}",
decryption_error="Failed to decrypt API key. The encryption key may have changed.",
)
# Preserve the DB id, created, updated from the raw row
if row.get("id"):