Skip to content

fix: prevent duplicate action execution via staged-action claim/idempotency guard #43

Description

@coderabbitai

Summary

The shared execute_action endpoint (PATCH /api/chat/messages/{message_id}/actions/{action_id}/execute) does not guard against re-execution of an already-executed action before invoking side-effectful handlers. This means a retry, double-click, or concurrent request can trigger the same side effect (e.g., creating a duplicate project issue, calendar event, or sending a duplicate email) from the same staged action.

Root cause

In core-api/api/routers/chat.py, the execute_action endpoint:

  1. Loads the action part from content_parts by action_id.
  2. Calls _execute_action (which calls the write service, e.g., create_issue).
  3. Only after the handler returns sets action_part["data"]["status"] = "executed" and persists it.

There is no check that action.status == "staged" before step 2, and no atomic compare-and-set (staged → executing) to claim the action before the write.

Impact

Any side-effectful handler is replayable:

  • create_project_issue → duplicate project cards
  • create_calendar_event → duplicate calendar events
  • send_email → duplicate emails sent

Suggested fix

Before calling _execute_action, add a status pre-check (or an atomic compare-and-set update) in the execute_action endpoint:

For stronger protection against concurrent requests, perform an atomic DB update that sets status = 'executing' only where status = 'staged' and check the row count before proceeding with the write.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions