Skip to content

[DEFECT] Content Drive: a folder can be duplicated while another user is deleting it, and the delete rolls back #37895

Open

Description

@rjvelazco

Problem Statement

Content Drive lets a user duplicate a folder that another user is deleting. The duplicate's copy can land inside the folder being deleted, and then the whole delete fails and rolls back with a raw database error. Neither user is warned.

Nothing coordinates the two operations:

The Content Drive UI marks folders being deleted as busy (with-folder-delete-runs.ts, allBusyRows, read from the server's list of active delete runs). The duplicate endpoint does not check, and the busy marking is read when the page loads, so it may not cover a delete another user starts afterwards.

Impact:

  • One user's duplicate undoes another user's delete. In our test the delete ran for ~2.5 minutes on 15,000 files and then rolled back completely. A 20,000-file folder took ~8 minutes to delete in an earlier measurement.
  • The deleting user gets a raw SQL error with no hint that someone else caused it.
  • More generally, any change to content while it is being deleted is unsafe. While a delete is running, nothing else should act on what it covers.

Steps to Reproduce

Local stack, dotcms/dotcms:trunk_d4a354c, site demo.dotcms.com.

  1. Create /qa-delete-long/ with a subfolder child/ (5 files) and enough content to make the delete take a few minutes (we used 15,000 small files under /qa-delete-long/bulk/).
  2. As user A (admin), start the delete:
    POST /api/v1/assets/folders/_bulkdelete with {"assetPaths":["//demo.dotcms.com/qa-delete-long/"]} → 202.
  3. Wait until the delete job is RUNNING (GET /api/v1/jobs/{jobId}/status).
  4. As user B (another back-end user), duplicate the subfolder:
    POST /api/v1/assets/folders/_bulkduplicate with {"assetPaths":["//demo.dotcms.com/qa-delete-long/child/"]}.

Actual:

Time Event
09:24:22 The delete starts running
~09:24:24 User B's duplicate is accepted with 202: no refusal, no warning
~09:24:50 The duplicate runs and creates child_copy inside /qa-delete-long/. Its job ends SUCCESS.
09:27:09 The delete reaches the end and fails. Its job reports the folder as FAILED / UNCLASSIFIED: com.dotmarketing.exception.DotDataException: ERROR: Cannot delete as this path has children Where: PL/pgSQL function check_child_assets() line 13 at RAISE SQL statement "DELETE from identifier where …"

End state: /qa-delete-long/ is intact with 15,012 files (the original 15,007 plus the 5 copied into child_copy). The whole delete was rolled back.

Expected: while a delete is running, the duplicate is refused at submission with a clear message that names the operation and who started it, for example:

"This folder is being deleted by Admin User. It can't be duplicated right now."

Acceptance Criteria

dotCMS Version

Latest from main: dotcms/dotcms:trunk_d4a354c (2026-10-02), which includes #37760, #37761 and the bulk delete from #37063.

Severity

High - Major functionality broken

Links

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions