Description
Description
For a One-Time Communication Flow, the ProcessCommunicationFlows job creates a CommunicationFlowInstance on its first run against the flow. From that point forward, changing the flow's Flow Start Date & Time in the UI has no effect. The instance keeps its original StartDate, and all message send times continue to be calculated from that stale date.
The flow editor displays the new start date, the job reports Success, and nothing sends. There is no error, no warning, and no indication in the UI that the displayed start date is not the one being honored.
Actual Use Case
We created a communication flow that was going to be activated once after an event. I created the flow so my team could then create the emails within them. I guessed at the activation date, assuming I could change it later. After we landed on the best communication plan, I needed to change the activation date up three days (so from August 31 to August 28). However, the instance had already been created with August 31st, so our email on the 28th didn't send. Did some digging with Claude, and it helped me understand that the instance had been created already.
Once the first communication sends, I would not expect changing the date to matter. This really only applies when you change the activation date prior to the first communication being sent.
Detail Provided by AI
Root Cause
ProcessCommunicationFlows.OneTimeTriggerTypeProcessor.EnsureFlowHasLatestInstance returns immediately if any instance exists, and never reconciles that instance against the flow's current schedule:
public void EnsureFlowHasLatestInstance( CommunicationFlow flow )
{
if ( flow.CommunicationFlowInstances.Any() )
{
// There is already a one-time flow instance so return.
return;
}
// No instances yet so try to create one.
var schedule = flow.Schedule;
...
var instance = new CommunicationFlowInstance
{
CommunicationFlowId = flow.Id,
CommunicationFlow = flow,
StartDate = firstStartDateTime.Value
};
...
}
Downstream, InstanceCommunicationHelper.CreateNextCommunication calculates the first message's send time from instance.StartDate, then bails when that date is not today:
nextCommunicationSendDateTime = instance
.StartDate
.AddDays( nextUnsentBlueprint.DaysToWait )
.Add( nextUnsentBlueprint.TimeToSend );
...
if ( nextCommunicationSendDateTime.Date != RockDateTime.Now.Date )
{
// Let the next job execution process the communication ...
communication = null;
return;
}
Because the stale StartDate is in the future relative to the edited schedule, this guard is hit on every job run and returns silently. Nothing is logged and the job still reports success.
Additional Observation (we have not experienced or reproduced this)
The following was surfaced by an AI code review of ProcessCommunicationFlows while diagnosing the issue above. We have not run into it ourselves and did not attempt to reproduce it. Including it only in case it is useful to whoever picks up the issue.
The first message of a One-Time flow may be offset by the flow's start time. OneTimeTriggerTypeProcessor sets StartDate = firstStartDateTime.Value, preserving the time component. RecurringTriggerTypeProcessor sets StartDate = nextInstanceStartDateTime.Value.Date, stripping it. Every subsequent message in CreateNextCommunication is calculated from lastCommunicationSendDateTime.Value.Date, also stripped.
That leaves the first message of a One-Time flow as the only calculation that adds TimeToSend on top of a non-midnight base. On paper, a flow starting at 8:00 AM with a first message set to "Same Day, Send at 10:00 AM" would compute to 6:00 PM. If the sum crossed midnight, the nextCommunicationSendDateTime.Date != RockDateTime.Now.Date guard would push the message to the following day.
Again: theoretical, from code reading only. Not observed on our install.
Also flagged by the same review, also unverified: in ProcessCommunicationFlows.Execute(), the guard for a flow that becomes inactive mid-run uses return rather than continue, which would exit the entire job loop and skip all remaining flows rather than skipping just that one.
// Check again if flow is active in case it changed while this job was running.
if ( communicationFlow == null || !communicationFlow.IsActive )
{
return;
}
Actual Behavior
Nothing sends on Aug. 29th. The flow instance still carries StartDate = Sep. 4th:
Expected Behavior
The first message sends on Aug. 29th at 10:30 AM (the date/time the flow was updated to activate), matching what the flow editor displays.
Steps to Reproduce
- Create a new Communication Flow. Set Flow Type to One-Time.
- Set Target Audience to a Data View that returns at least one person.
- Set Flow Start Date & Time to a date a few days in the future. For this example use Sep. 4th.
- Add at least one message to the flow. Set the first message to Same Day, send at 10:30 AM. (I enabled conversion tracking because that is what our system had enabled, but I don't think it is relevant)
- Save and activate the flow.
- Allow the
Process Communication Flows job to run at least once, or run it manually from Admin Tools > System Settings > Jobs Administration.
- Confirm the instance was created and holds the Sep. 4th start date:
SELECT i.Id, i.StartDate, i.IsMessagingCompleted
FROM CommunicationFlowInstance i
--WHERE i.CommunicationFlowId = <FlowId>; -- add this if more than one flow exists and you need clarity
- Return to the flow editor. Change Flow Start Date & Time to a different date, earlier than Sep. 4th. Use Aug. 29th (today). Save.
- Confirm the schedule record actually updated:
SELECT s.Id, s.EffectiveStartDate, s.iCalendarContent
FROM Schedule s
JOIN CommunicationFlow f ON f.ScheduleId = s.Id
--WHERE f.Id = <FlowId>; -- add this if more than one flow exists and you need clarity
- Let the job run through Aug. 29th and past the configured 10:00 AM send time.
SELECT i.Id, i.StartDate FROM CommunicationFlowInstance i
WHERE i.CommunicationFlowId = <FlowId>;
-- StartDate is still Day X
The flow will eventually send on Sep. 4th, the original date, with no explanation available to an administrator looking at the UI.

Issue Confirmation
Rock Version
Rock McKinley 19.3 (19.3.4)
Client Culture Setting
en-US
Description
Description
For a One-Time Communication Flow, the
ProcessCommunicationFlowsjob creates aCommunicationFlowInstanceon its first run against the flow. From that point forward, changing the flow's Flow Start Date & Time in the UI has no effect. The instance keeps its originalStartDate, and all message send times continue to be calculated from that stale date.The flow editor displays the new start date, the job reports
Success, and nothing sends. There is no error, no warning, and no indication in the UI that the displayed start date is not the one being honored.Actual Use Case
We created a communication flow that was going to be activated once after an event. I created the flow so my team could then create the emails within them. I guessed at the activation date, assuming I could change it later. After we landed on the best communication plan, I needed to change the activation date up three days (so from August 31 to August 28). However, the instance had already been created with August 31st, so our email on the 28th didn't send. Did some digging with Claude, and it helped me understand that the instance had been created already.
Once the first communication sends, I would not expect changing the date to matter. This really only applies when you change the activation date prior to the first communication being sent.
Detail Provided by AI
Root Cause
ProcessCommunicationFlows.OneTimeTriggerTypeProcessor.EnsureFlowHasLatestInstancereturns immediately if any instance exists, and never reconciles that instance against the flow's current schedule:Downstream,
InstanceCommunicationHelper.CreateNextCommunicationcalculates the first message's send time frominstance.StartDate, then bails when that date is not today:Because the stale
StartDateis in the future relative to the edited schedule, this guard is hit on every job run and returns silently. Nothing is logged and the job still reports success.Additional Observation (we have not experienced or reproduced this)
The following was surfaced by an AI code review of
ProcessCommunicationFlowswhile diagnosing the issue above. We have not run into it ourselves and did not attempt to reproduce it. Including it only in case it is useful to whoever picks up the issue.The first message of a One-Time flow may be offset by the flow's start time.
OneTimeTriggerTypeProcessorsetsStartDate=firstStartDateTime.Value, preserving the time component.RecurringTriggerTypeProcessorsetsStartDate = nextInstanceStartDateTime.Value.Date, stripping it. Every subsequent message inCreateNextCommunicationis calculated fromlastCommunicationSendDateTime.Value.Date, also stripped.That leaves the first message of a One-Time flow as the only calculation that adds
TimeToSendon top of a non-midnight base. On paper, a flow starting at 8:00 AM with a first message set to "Same Day, Send at 10:00 AM" would compute to 6:00 PM. If the sum crossed midnight, thenextCommunicationSendDateTime.Date != RockDateTime.Now.Dateguard would push the message to the following day.Again: theoretical, from code reading only. Not observed on our install.
Also flagged by the same review, also unverified: in
ProcessCommunicationFlows.Execute(), the guard for a flow that becomes inactive mid-run usesreturnrather thancontinue, which would exit the entire job loop and skip all remaining flows rather than skipping just that one.Actual Behavior
Nothing sends on Aug. 29th. The flow instance still carries
StartDate = Sep. 4th:Expected Behavior
The first message sends on Aug. 29th at 10:30 AM (the date/time the flow was updated to activate), matching what the flow editor displays.
Steps to Reproduce
Process Communication Flowsjob to run at least once, or run it manually from Admin Tools > System Settings > Jobs Administration.The flow will eventually send on Sep. 4th, the original date, with no explanation available to an administrator looking at the UI.

Issue Confirmation
Rock Version
Rock McKinley 19.3 (19.3.4)
Client Culture Setting
en-US