Skip to content

upstream PR #15684: project file attachment: a failed hold release leaves the attached file's upload TTL armed #18

Closed as not planned

Description

@berry-13

Follow-up from LibreChat-AI#15684. Originally filed as LibreChat-AI#15804 (to be closed there; follow-ups live on this fork).


What happens

addChatProjectFile associates a file with a Chat Project through one atomic
findOneAndUpdate (capacity guarded), and only afterwards clears the upload
hold on the file document:

// packages/data-schemas/src/methods/chatProject.ts
const updated = await ChatProject.findOneAndUpdate(
  { _id: project._id, user, file_ids: { $ne: fileId },
    [`file_ids.${MAX_CHAT_PROJECT_FILES - 1}`]: { $exists: false } },
  { $addToSet: { file_ids: fileId }, $inc: { contextRevision: 1 } },
  ...
);
if (updated) {
  await releaseTemporaryHold();   // $unset expiresAt, temp_file_id
  return updated;
}

That ordering is deliberate: a racer that loses the capacity guard must keep its
hold so the sweep can reclaim the upload (PR LibreChat-AI#15684). The two writes span two
collections without a transaction, so if the release write fails after the
association commits, the project keeps the reference while the file keeps
expiresAt — a MongoDB-managed one hour TTL (packages/data-schemas/src/schema/file.ts).
The request surfaces as a 500 from addProjectFile, and the file can be deleted
underneath a project that still lists it.

Why it is not already broken

An idempotent re-add repairs it: the atomic update no-ops, the fresh read finds
the file already associated, and the release is retried
(chatProject.ts, fresh-current branch). The client retries the association on
its failed row, so the common path self-heals. A user who never retries keeps a
project reference that resolves as unavailable after the TTL fires.

Suggested direction

Compensate the release rather than relying on a retry: bounded inline retry, or
a reconciliation pass that clears holds on files already referenced by a project
(the sweep is expiredAt-driven and does not look at project membership). Worth
a regression that forces the release write to fail and asserts the file does not
stay TTL-armed.

Found while auditing PR LibreChat-AI#15684; no regression covers this window today.

Activity

  1. berry-13 commented on Sep 9, 2026

    @berry-13
    OwnerAuthor

    Duplicate of the issue the working agent filed; keeping theirs.

  2. added
    bugSomething isn't working
    duplicateThis issue or pull request already exists
    on Sep 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: packagesreviewctl followups statebugSomething isn't workingduplicateThis issue or pull request already exists

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions