{"record":{"id":"6d1c03503dbdd7b7","repo":"stride3d/stride","slug":"microthread-completed-in-an-invalid-state","errorCode":null,"errorMessage":"MicroThread completed in an invalid state.","messagePattern":"MicroThread completed in an invalid state\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/core/Stride.Core.MicroThreading/MicroThread.cs","lineNumber":177,"sourceCode":"    /// <param name=\"microThreadFunction\">The micro thread function.</param>\n    /// <param name=\"scheduleMode\">The schedule mode.</param>\n    /// <exception cref=\"System.InvalidOperationException\">MicroThread was already started before.</exception>\n    public void Start(Func<Task> microThreadFunction, ScheduleMode scheduleMode = ScheduleMode.Last)\n    {\n        // TODO: Interlocked compare exchange?\n        if (Interlocked.CompareExchange(ref state, (int)MicroThreadState.Starting, (int)MicroThreadState.None) != (int)MicroThreadState.None)\n            throw new InvalidOperationException(\"MicroThread was already started before.\");\n\n        Func<Task> wrappedMicroThreadFunction = async () =>\n        {\n            try\n            {\n                State = MicroThreadState.Running;\n\n                await microThreadFunction();\n\n                if (State != MicroThreadState.Running)\n                    throw new InvalidOperationException(\"MicroThread completed in an invalid state.\");\n                State = MicroThreadState.Completed;\n            }\n            catch (OperationCanceledException e)\n            {\n                // Exit gracefully on cancellation exceptions\n                SetException(e);\n            }\n            catch (Exception e)\n            {\n                Scheduler.Log.Error(\"Unexpected exception while executing a micro-thread.\", e);\n                SetException(e);\n            }\n            finally\n            {\n                lock (Scheduler.AllMicroThreads)\n                {\n                    Scheduler.AllMicroThreads.Remove(AllLinkedListNode);\n                }","sourceCodeStart":159,"sourceCodeEnd":195,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/core/Stride.Core.MicroThreading/MicroThread.cs#L159-L195","documentation":"After the micro-thread's function completes, the wrapper asserts the state is still Running before marking it Completed. If the body (or its continuations) already changed State — e.g. set it to Canceled/Failed or completed via another path — Start throws InvalidOperationException 'MicroThread completed in an invalid state.'","triggerScenarios":"The awaited microThreadFunction returned after code inside it modified State directly, called Unschedule/completion APIs, or nested completion logic mutated the state machine.","commonSituations":"User script code calling low-level state setters or completion methods inside a micro-thread; duplicate completion logic firing when the function resolves; cancellation racing with completion.","solutions":["Remove code inside the micro-thread body that mutates MicroThread.State or triggers completion directly","Let cancellation propagate via OperationCanceledException instead of manually setting state","Ensure the awaited function doesn't complete the micro-thread itself (e.g. double-awaiting a completion task)"],"exampleFix":"// before\nState = MicroThreadState.Completed; // inside micro-thread body\n// after\n// do nothing; framework sets Completed when the awaited function returns","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await microThreadFunction(); }\ncatch (InvalidOperationException) { /* state was mutated inside body; audit body for direct State writes */ }","preventionTips":["Never set MicroThread.State inside the body","Use OperationCanceledException for cancellation","Avoid double-completion paths"],"tags":["csharp","microthreading","state-machine","stride"],"backgroundTag":"internal-invariant-violation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}