stride3d/stride · error · InvalidOperationException
MicroThread completed in an invalid state.
Error message
MicroThread completed in an invalid state.
What it means
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.'
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)
Example fix
// before State = MicroThreadState.Completed; // inside micro-thread body // after // do nothing; framework sets Completed when the awaited function returns
Defensive patterns
Strategy: try-catch
Try / catch
try { await microThreadFunction(); }
catch (InvalidOperationException) { /* state was mutated inside body; audit body for direct State writes */ } Prevention
- Never set MicroThread.State inside the body
- Use OperationCanceledException for cancellation
- Avoid double-completion paths
When it happens
Trigger: The awaited microThreadFunction returned after code inside it modified State directly, called Unschedule/completion APIs, or nested completion logic mutated the state machine.
Common situations: 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.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Cannot receive out of micro-thread context.
- MicroThread was already started before.
- MicroThread is already completed but still posting…
- NextFrame cannot be called out of the micro-thread context.
- Already scheduled, call Unschedule before running this…
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/6d1c03503dbdd7b7.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.MicroThreading/MicroThread.cs:177
/// <param name="microThreadFunction">The micro thread function.</param>
/// <param name="scheduleMode">The schedule mode.</param>
/// <exception cref="System.InvalidOperationException">MicroThread was already started before.</exception>
public void Start(Func<Task> microThreadFunction, ScheduleMode scheduleMode = ScheduleMode.Last)
{
// TODO: Interlocked compare exchange?
if (Interlocked.CompareExchange(ref state, (int)MicroThreadState.Starting, (int)MicroThreadState.None) != (int)MicroThreadState.None)
throw new InvalidOperationException("MicroThread was already started before.");
Func<Task> wrappedMicroThreadFunction = async () =>
{
try
{
State = MicroThreadState.Running;
await microThreadFunction();
if (State != MicroThreadState.Running)
throw new InvalidOperationException("MicroThread completed in an invalid state.");
State = MicroThreadState.Completed;
}
catch (OperationCanceledException e)
{
// Exit gracefully on cancellation exceptions
SetException(e);
}
catch (Exception e)
{
Scheduler.Log.Error("Unexpected exception while executing a micro-thread.", e);
SetException(e);
}
finally
{
lock (Scheduler.AllMicroThreads)
{
Scheduler.AllMicroThreads.Remove(AllLinkedListNode);
}View on GitHub (pinned to 96fad776d2)