Problem
The orchestration executor's processEvent switch statement in packages/durabletask-js/src/worker/orchestration-executor.ts (line ~131) is missing a case for pb.HistoryEvent.EventtypeCase.EVENTSENT (enum value 16). When an orchestrator calls ctx.sendEvent(), the sidecar records an EventSentEvent history event to confirm the action was processed. On the next replay, this event falls through to the default case and is logged as an unknown event type.
Without a handler, the sendEvent action remains in _pendingActions and is returned to the sidecar again via getActions(), potentially causing duplicate event delivery to the target orchestration instance.
Root Cause
All other "confirmation" events have dedicated handlers that remove actions from _pendingActions:
TASKSCHEDULED → handleTaskScheduled removes the ScheduleTask action
TIMERCREATED → handleTimerCreated removes the CreateTimer action
SUBORCHESTRATIONINSTANCECREATED → handleSubOrchestrationCreated removes the action
ENTITYOPERATIONCALLED/ENTITYOPERATIONSIGNALED → validateEntityAction removes the action
EVENTSENT (enum 16) was missed from this pattern.
Note: This is analogous to issue #235 (ENTITYUNLOCKSENT) but for a different event type.
Proposed Fix
Add a handleEventSent case to the switch statement that:
- Retrieves the action from
_pendingActions by event ID
- Deletes it from
_pendingActions
- Validates it is a
sendEvent action (throws NonDeterminismError otherwise)
Impact
Severity: Medium — On replay, orchestrations that call ctx.sendEvent() could cause the target instance to receive duplicate events. This affects any orchestration using fire-and-forget event delivery patterns.
Scenarios affected: Any orchestration that calls ctx.sendEvent() and subsequently gets replayed (e.g., after yielding a timer or activity after the sendEvent call).
Problem
The orchestration executor's
processEventswitch statement inpackages/durabletask-js/src/worker/orchestration-executor.ts(line ~131) is missing a case forpb.HistoryEvent.EventtypeCase.EVENTSENT(enum value 16). When an orchestrator callsctx.sendEvent(), the sidecar records anEventSentEventhistory event to confirm the action was processed. On the next replay, this event falls through to thedefaultcase and is logged as an unknown event type.Without a handler, the
sendEventaction remains in_pendingActionsand is returned to the sidecar again viagetActions(), potentially causing duplicate event delivery to the target orchestration instance.Root Cause
All other "confirmation" events have dedicated handlers that remove actions from
_pendingActions:TASKSCHEDULED→handleTaskScheduledremoves theScheduleTaskactionTIMERCREATED→handleTimerCreatedremoves theCreateTimeractionSUBORCHESTRATIONINSTANCECREATED→handleSubOrchestrationCreatedremoves the actionENTITYOPERATIONCALLED/ENTITYOPERATIONSIGNALED→validateEntityActionremoves the actionEVENTSENT(enum 16) was missed from this pattern.Note: This is analogous to issue #235 (
ENTITYUNLOCKSENT) but for a different event type.Proposed Fix
Add a
handleEventSentcase to the switch statement that:_pendingActionsby event ID_pendingActionssendEventaction (throwsNonDeterminismErrorotherwise)Impact
Severity: Medium — On replay, orchestrations that call
ctx.sendEvent()could cause the target instance to receive duplicate events. This affects any orchestration using fire-and-forget event delivery patterns.Scenarios affected: Any orchestration that calls
ctx.sendEvent()and subsequently gets replayed (e.g., after yielding a timer or activity after the sendEvent call).