fix(core): validate file existence before creating files_agents rows (#9341)

Prevents ForeignKeyViolationError when attaching files to agents where
the file has been deleted between listing and attachment (race condition).
Now validates file IDs exist in the files table before inserting, and
skips any missing files with a warning log.

Fixes Datadog issue a1768774-d691-11f0-9330-da7ad0900000

🐾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
Kian Jones
2026-02-06 00:47:03 -08:00
committed by Caren Thomas
parent cb982cae89
commit 2cfaba3fe6

View File

@@ -5,6 +5,7 @@ from sqlalchemy import and_, delete, func, or_, select, update
from letta.log import get_logger from letta.log import get_logger
from letta.orm.errors import NoResultFound from letta.orm.errors import NoResultFound
from letta.orm.file import FileMetadata as FileMetadataModel
from letta.orm.files_agents import FileAgent as FileAgentModel from letta.orm.files_agents import FileAgent as FileAgentModel
from letta.otel.tracing import trace_method from letta.otel.tracing import trace_method
from letta.schemas.block import Block as PydanticBlock, FileBlock as PydanticFileBlock from letta.schemas.block import Block as PydanticBlock, FileBlock as PydanticFileBlock
@@ -696,6 +697,20 @@ class FileAgentManager:
closed_file_names.extend(new_names[max_files_open:]) closed_file_names.extend(new_names[max_files_open:])
evicted_ids = [r.file_id for r in currently_open if r.file_name in closed_file_names] evicted_ids = [r.file_id for r in currently_open if r.file_name in closed_file_names]
# validate file IDs exist to prevent FK violations (files may have been deleted)
requested_file_ids = {meta.id for meta in ordered_unique}
existing_file_ids_q = select(FileMetadataModel.id).where(FileMetadataModel.id.in_(requested_file_ids))
existing_file_ids = set((await session.execute(existing_file_ids_q)).scalars().all())
missing_file_ids = requested_file_ids - existing_file_ids
if missing_file_ids:
logger.warning(
"attach_files_bulk: skipping %d file(s) with missing records for agent %s: %s",
len(missing_file_ids),
agent_id,
missing_file_ids,
)
ordered_unique = [m for m in ordered_unique if m.id in existing_file_ids]
# upsert requested files # upsert requested files
for meta in ordered_unique: for meta in ordered_unique:
is_now_open = meta.file_name in final_open_set is_now_open = meta.file_name in final_open_set