{"record":{"id":"7a719f8f714df349","repo":"elsa-workflows/elsa-core","slug":"signal-signal-timed-out-after-millisecondstimeout","errorCode":null,"errorMessage":"Signal '{signal}' timed out after {millisecondsTimeout} milliseconds.","messagePattern":"Signal '(.+?)' timed out after (.+?) milliseconds\\.","errorType":"exception","errorClass":"TimeoutException","httpStatus":null,"severity":"error","filePath":"src/common/Elsa.Testing.Shared.Component/Services/SignalManager.cs","lineNumber":29,"sourceCode":"        var result = await WaitAsync(signal, millisecondsTimeout);\n        \n        if(result is not T typedResult)\n            throw new InvalidCastException($\"Signal '{signal}' was not of type '{typeof(T).Name}'.\");\n\n        return typedResult;\n    }\n\n    public async Task<object?> WaitAsync(object signal, int millisecondsTimeout = 60000)\n    {\n        var taskCompletionSource = GetOrCreate(signal);\n        using var cancellationTokenSource = new CancellationTokenSource(millisecondsTimeout);\n        var delayTask = Task.Delay(millisecondsTimeout, cancellationTokenSource.Token);\n        var completedTask = await Task.WhenAny(taskCompletionSource.Task, delayTask);\n\n        if (completedTask == delayTask)\n        {\n            _signals.TryRemove(signal, out _);\n            throw new TimeoutException($\"Signal '{signal}' timed out after {millisecondsTimeout} milliseconds.\");\n        }\n\n        cancellationTokenSource.Cancel();\n        _signals.TryRemove(signal, out _);\n        return await taskCompletionSource.Task;\n    }\n\n    public void Trigger(object signal, object? result = null)\n    {\n        var taskCompletionSource = GetOrCreate(signal);\n\n        if (taskCompletionSource.Task.IsCompleted)\n            return;\n\n        taskCompletionSource.SetResult(result);\n    }\n\n    private TaskCompletionSource<object?> GetOrCreate(object eventName)","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/common/Elsa.Testing.Shared.Component/Services/SignalManager.cs#L11-L47","documentation":"SignalManager.WaitAsync waits on a TaskCompletionSource for the signal while a Task.Delay races against it; when the delay wins, the pending signal entry is removed and a TimeoutException is thrown. This means the awaited signal was never triggered (or was triggered before the wait started / with a different name) within the configured timeout (default 60000 ms).","triggerScenarios":"Calling signalManager.WaitAsync(\"signal-name\") when no code calls TriggerSignal(\"signal-name\") within the timeout; the signal was triggered before WaitAsync registered its TCS; workflow failed or never reached the signaling activity; timeout parameter set too low for a slow workflow.","commonSituations":"The workflow under test throws or blocks before reaching the signal; a typo or case mismatch between signal names in trigger and wait; parallel tests sharing one SignalManager steal each other's signals; CI machines are slower than the hard-coded timeout.","solutions":["Verify the workflow actually calls TriggerSignal with the exact same signal name (check spelling and casing).","Ensure WaitAsync is called before or concurrently with the workflow run so the TCS is registered before the signal fires.","Increase the millisecondsTimeout argument (or default 60000) when workflows are slow, especially in CI.","Check workflow execution logs for faults that prevent the signal activity from running; ensure a fresh SignalManager per test to avoid cross-test signal leakage."],"exampleFix":"// before\nvar result = await signals.WaitAsync<string>(\"approved\"); // times out at 60s\n// after\nawait workflowClient.RunWorkflowAsync(...); // ensure run actually reaches TriggerSignal(\"approved\")\nvar result = await signals.WaitAsync<string>(\"approved\", 120000);","handlingStrategy":"try-catch","validationCode":"// confirm the workflow will signal before waiting\nAssert.True(workflowDefinition.Activities.Any(a => a.Type == \"Signal\" && a.GetSignalName() == \"approved\"),\n    \"Workflow does not contain an activity that triggers signal 'approved'.\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var result = await signals.WaitAsync<string>(\"approved\", 120000);\n}\ncatch (TimeoutException ex)\n{\n    Assert.Fail($\"Signal never arrived: {ex.Message}. Check workflow faulted state and signal name.\");\n}","preventionTips":["Call WaitAsync before starting the workflow so the TCS is registered first.","Use generous, environment-aware timeouts (CI is slower than local).","Use one SignalManager per test to prevent cross-test signal theft.","Assert signal names with constants shared between workflow and test."],"tags":["testing","signals","timeout"],"backgroundTag":"request-timeout","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}