stride3d/stride · error · Exception
NextUpdate cannot be called out of the micro-thread context.
Error message
NextUpdate cannot be called out of the micro-thread context.
What it means
NextUpdate() yields the current micro-thread's execution until just before the next physics tick. It requires the caller to be running inside a Stride micro-thread (Scheduler.CurrentMicroThread) with a SynchronizationContext, because resumption is implemented via TickAwaiter tied to that context. Calling it from a plain thread, Task, or Unity-style context throws.
Solutions
- Call NextUpdate() only from code running on a Stride micro-thread (e.g. inside an async Script execution / ScriptComponent async method started by the engine).
- For non-micro-thread code, subscribe to the simulation's tick events or poll simulation state instead of awaiting NextUpdate().
- If awaiting is needed elsewhere, marshal to the engine context first (e.g. via the script's scheduler) before calling.
Example fix
// before (plain thread)
Task.Run(async () => await sim.NextUpdate());
// after (inside an async Stride script)
public async Task Execute() {
var awaiter = sim.NextUpdate(); // running on a micro-thread
} Defensive patterns
Strategy: validation
Validate before calling
if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null) {
// not on a micro-thread: do not call NextUpdate()
return;
}
var awaiter = sim.NextUpdate(); Try / catch
try { var a = sim.NextUpdate(); } catch (Exception ex) { log.Warn("NextUpdate requires micro-thread context", ex); /* fall back to event/polling */ } Prevention
- Only call tick-await APIs from async Stride scripts running on micro-threads
- Never wrap NextUpdate in Task.Run or background threads
- Use simulation events for code that lives outside the engine context
When it happens
Trigger: Calling BepuSimulation.NextUpdate() from a regular async Task method, a background thread, a console app main method, or anywhere Scheduler.CurrentMicroThread is null or SynchronizationContext.Current is null.
Common situations: Developers trying to synchronize gameplay code with physics from a non-script context (unit tests, threads, timers) instead of from Stride script/micro-thread execution.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- AfterUpdate cannot be called out of the micro-thread…
- A Gear constraint always needs two rigidbodies to be…
- A Gear constraint requires two rigidbodies.
- All should have hashsets associated to them
- ArgumentNullException: data
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/650aabb3e3083098.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs:367
Debug.Assert(body is not null, "Handle is invalid, Bepu's array indexing strategy might have changed under us");
return body;
}
public StaticComponent GetComponent(StaticHandle handle)
{
var statics = Statics[handle.Value];
Debug.Assert(statics is not null, "Handle is invalid, Bepu's array indexing strategy might have changed under us");
return statics;
}
/// <summary>
/// Yields execution until right before the next physics tick
/// </summary>
/// <returns>Task that will resume next tick.</returns>
public TickAwaiter NextUpdate()
{
if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null)
throw new Exception($"{nameof(NextUpdate)} cannot be called out of the micro-thread context.");
return new TickAwaiter(_preTickRunner, Scheduler.CurrentMicroThread, SynchronizationContext.Current);
}
/// <summary>
/// Yields execution until right after the next physics tick
/// </summary>
/// <returns>Task that will resume next tick.</returns>
public TickAwaiter AfterUpdate()
{
if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null)
throw new Exception($"{nameof(AfterUpdate)} cannot be called out of the micro-thread context.");
return new TickAwaiter(_postTickRunner, Scheduler.CurrentMicroThread, SynchronizationContext.Current);
}
/// <summary>
/// Whether a physics test with <paramref name="mask"/> against <paramref name="collidable"/> should be performed or entirely ignored
/// </summary>
/// <returns>True when it should be performed, false when it should be ignored</returns>View on GitHub (pinned to 96fad776d2)