From 2cfaba3fe64906e390980fa1d6c5616dd48f473e Mon Sep 17 00:00:00 2001 From: Kian Jones <11655409+kianjones9@users.noreply.github.com> Date: Fri, 6 Feb 2026 00:47:03 -0800 Subject: [PATCH] fix(core): validate file existence before creating files_agents rows (#9341) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- letta/services/files_agents_manager.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/letta/services/files_agents_manager.py b/letta/services/files_agents_manager.py index d45947a9..4d053ade 100644 --- a/letta/services/files_agents_manager.py +++ b/letta/services/files_agents_manager.py @@ -5,6 +5,7 @@ from sqlalchemy import and_, delete, func, or_, select, update from letta.log import get_logger 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.otel.tracing import trace_method 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:]) 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 for meta in ordered_unique: is_now_open = meta.file_name in final_open_set