stride3d/stride · error · Exception
NextFrame cannot be called out of the micro-thread context.
Error message
NextFrame cannot be called out of the micro-thread context.
What it means
Scheduler.NextFrame() returns an awaiter tied to the frame Channel; receiving from that channel requires a running micro-thread to suspend and resume. Called outside micro-thread context (MicroThread.Current == null), it throws because frame waits cannot be scheduled.
Solutions
- Call NextFrame only inside micro-thread code (AsyncScript/SyncScript or Scheduler.Spawn)
- For non-micro-thread code, use the engine's Update loop or an event/callback pattern instead of frame yielding
- Wrap the frame-yielding logic in a micro-thread
Example fix
// before
public async Task Loop(Scheduler s) { while (true) await s.NextFrame(); } // run via Task
// after
await Scheduler.Spawn(async mt => { while (true) await Scheduler.NextFrame(); }); Defensive patterns
Strategy: type-guard
Validate before calling
if (Stride.Core.MicroThreading.MicroThread.Current == null) throw new NotSupportedException("NextFrame requires micro-thread context"); Type guard
bool canNextFrame = MicroThread.Current != null;
Try / catch
try { await scheduler.NextFrame(); }
catch (Exception) when (MicroThread.Current == null) { /* use update loop instead */ } Prevention
- Call NextFrame only in scripts/micro-threads
- Use game Update loop for non-micro-thread code
- Wrap frame-looping logic in Scheduler.Spawn
When it happens
Trigger: Calling scheduler.NextFrame() from a normal Task, thread, UI event handler, or constructor rather than inside a micro-thread/script.
Common situations: Using NextFrame in unit tests without spawning a micro-thread; awaiting frames in utility classes invoked from non-script code; migration from async/await on Task to Stride micro-threads.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot receive out of micro-thread context.
- MicroThread was already started before.
- MicroThread completed in an invalid state.
- MicroThread is already completed but still posting…
- Already scheduled, call Unschedule before running this…
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/680c265721f761e1.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.MicroThreading/Scheduler.cs:108
/// <summary>
/// Yields execution.
/// If any other micro thread is pending, it will be run now and current micro thread will be scheduled as last.
/// </summary>
/// <returns>Task that will resume later during same frame.</returns>
public static MicroThreadYieldAwaiter Yield()
{
return new MicroThreadYieldAwaiter(CurrentMicroThread);
}
/// <summary>
/// Yields execution until next frame.
/// </summary>
/// <returns>Task that will resume next frame.</returns>
public ChannelMicroThreadAwaiter<int> NextFrame()
{
if (MicroThread.Current == null)
throw new Exception("NextFrame cannot be called out of the micro-thread context.");
return FrameChannel.Receive();
}
/// <summary>
/// Runs until no runnable tasklets left.
/// This function is reentrant.
/// </summary>
public void Run()
{
int managedThreadId = Environment.CurrentManagedThreadId;
MicroThreadCallbackList callbacks = default;
try
{
runRecursion++;
if (runRecursion == 1)View on GitHub (pinned to 96fad776d2)