Interaction Timeout
Every interaction has a deadline. If the user does not respond within timeoutMs, the pipeline fires an InteractionTimeout. Handling timeouts correctly is critical for building reliable workflows — never assume a response will arrive.
Timeout Flow
Timer Fires
The server-side timer for this interaction expires. The pipeline attempts to atomically lock the interaction. If a response arrived in the same millisecond (race condition), the response wins — the timeout is discarded.
Publish InteractionTimeout
The pipeline publishes an InteractionTimeout message to the callback topic interactions.callback.{interactionId}. The server's PublishAndWaitAsync() Task receives this and throws InteractionTimeoutException.
Dismiss Client UI
The pipeline publishes a dismissal signal to interactions.dismiss.{userId}. The client receives this, removes the interaction from the queue, and marks it as "Expired" in the history.
Audit Log
The audit hook logs the timeout event with the interactionId, timestamp, and "no response received" outcome. This creates a compliance record even for unanswered interactions.
InteractionTimeout Message
interface InteractionTimeout {
interactionId: string;
outcome: 'timeout';
timedOutAt: string; // ISO 8601
timeoutMs: number; // The original timeout setting
}
Handling Timeout on the Server
// C# — handling timeout in a workflow node
public async Task ExecuteAsync(NodeExecutionContext context)
{
try
{
var response = await _interactionPublisher.PublishAndWaitAsync(request);
// Handle the user's response
if (response.Outcome == "approved")
{
context.SetOutput("decision", "approved");
}
else if (response.Outcome == "rejected")
{
context.SetOutput("decision", "rejected");
context.SetOutput("rejectionComment", response.Data?["comment"]);
}
}
catch (InteractionTimeoutException ex)
{
// No response received within timeoutMs
// Route to timeout branch in the workflow
context.SetOutput("decision", "timeout");
context.SetOutput("timedOutAt", ex.TimedOutAt.ToString("O"));
// Optionally escalate — send to a manager, trigger an alert, etc.
await _escalationService.EscalateAsync(ex.InteractionId);
}
}
Timeout Strategies
| Strategy | Description | When to Use |
|---|---|---|
| Escalate | On timeout, send the same interaction to a higher-authority user or role | Approval chains — manager escalation |
| Auto-approve | On timeout, treat as approved (with audit log note) | Non-critical confirmations with SLA pressure |
| Auto-reject | On timeout, treat as rejected | Security-sensitive approvals — err on the side of caution |
| Fail the workflow | On timeout, terminate the workflow branch with an error | When no response means the process cannot continue |
| Retry | On timeout, re-publish the same interaction (new interactionId) | Reminders — "you missed this, please respond" |
Recommended Timeout Values
| Interaction Type | Recommended Timeout | Rationale |
|---|---|---|
| Approval (manager) | 24–48 hours | Managers may be unavailable for a full business day |
| Approval (real-time) | 30–60 minutes | User is online but may step away briefly |
| Confirmation (user action) | 5–15 minutes | User initiated the action and is likely present |
| Form collection | 30 minutes | Allow time to fill in the form without rushing |
| Picker | 10–30 minutes | Simple selection — should not take long |
| Notification (acknowledge) | 7 days | Compliance notifications — user must acknowledge but can do it later |
timeoutMs to an arbitrarily large value to avoid handling timeouts. This leaves pipeline resources (state store entries, callback subscriptions) open indefinitely. Always set a finite timeout and handle it explicitly.