Skip to content

[BUG][UI]: Race condition in deleteTeamSafe causes stale team list after deletion #2864

@crivetimihai

Description

@crivetimihai

Description

After deleting a team via the "Delete Team" button, the team list briefly (or persistently) shows stale data — the deleted team still appears in the list even though it was successfully removed on the server.

Root Cause

deleteTeamSafe() in admin.html triggers a double-refresh pattern that races with the database transaction:

htmx.ajax('DELETE', '/admin/teams/' + teamId, {
    target: '#unified-teams-list',
    swap: 'innerHTML'
}).then(() => {
    initializeTeamManagement(); // Refresh #1: fires ~41ms after DELETE response
});

The backend DELETE handler also returns an HX-Trigger header:

response.headers["HX-Trigger"] = orjson.dumps({
    "adminTeamAction": {"refreshUnifiedTeamsList": True, "delayMs": 1000}
}).decode()

This results in 3 competing operations targeting #unified-teams-list:

  1. DELETE response (t=0): Swaps a success message into the list container
  2. .then() GET (t+~41ms): Fetches the team list — may return stale data if the DB transaction hasn't fully committed
  3. HX-Trigger GET (t+~1035ms): Fetches the team list again — also may return stale data

Observed Behavior

  • Tested with Playwright against Docker deployment (nginx → gateway)
  • Deleted PaginationTest-17 from page 2 — page continued showing it with "15 items"
  • Server confirmed deletion (fetch to /admin/teams/partial?page=1&per_page=100 returned 14 teams, PaginationTest-17 absent)
  • Manual refresh via htmx.ajax correctly showed updated list with 14 items
  • Same behavior observed when deleting PaginationTest-18

Expected Behavior

After deleting a team, the team list should refresh and show the updated list without the deleted team.

Affected Functions

The same double-refresh pattern exists in multiple functions in admin.html:

  • deleteTeamSafe() (~line 14703)
  • leaveTeamSafe() (~line 14654)
  • cancelJoinRequest() (~line 14716)

All follow the pattern: htmx.ajax(...).then(() => initializeTeamManagement()) while the backend also fires adminTeamAction via HX-Trigger.

Suggested Fix

Two options:

Option A (minimal): Remove the .then(() => initializeTeamManagement()) from these functions and rely solely on the HX-Trigger event handler (handleAdminTeamAction) for refreshing. Increase the delay slightly to ensure the transaction has committed.

Option B (robust): Add a small delay before the refresh to allow the DB transaction to commit:

htmx.ajax('DELETE', '/admin/teams/' + teamId, {
    target: '#unified-teams-list',
    swap: 'innerHTML'
}).then(() => {
    setTimeout(() => initializeTeamManagement(), 500);
});

Environment

  • Docker deployment with nginx reverse proxy
  • SQLite database (may also affect PostgreSQL with different transaction isolation)
  • Tested on branch 2799-stay-on-the-same-page-when-updating-teams

Metadata

Metadata

Assignees

Labels

SHOULDP2: Important but not vital; high-value items that are not crucial for the immediate releasebugSomething isn't workingfixedIssue has been resolveduiUser Interface

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions