{"record":{"id":"650aabb3e3083098","repo":"stride3d/stride","slug":"nextupdate-cannot-be-called-out-of-the-micro-thread-context","errorCode":null,"errorMessage":"NextUpdate cannot be called out of the micro-thread context.","messagePattern":"NextUpdate cannot be called out of the micro-thread context\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs","lineNumber":367,"sourceCode":"        Debug.Assert(body is not null, \"Handle is invalid, Bepu's array indexing strategy might have changed under us\");\n        return body;\n    }\n\n    public StaticComponent GetComponent(StaticHandle handle)\n    {\n        var statics = Statics[handle.Value];\n        Debug.Assert(statics is not null, \"Handle is invalid, Bepu's array indexing strategy might have changed under us\");\n        return statics;\n    }\n\n    /// <summary>\n    /// Yields execution until right before the next physics tick\n    /// </summary>\n    /// <returns>Task that will resume next tick.</returns>\n    public TickAwaiter NextUpdate()\n    {\n        if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null)\n            throw new Exception($\"{nameof(NextUpdate)} cannot be called out of the micro-thread context.\");\n        return new TickAwaiter(_preTickRunner, Scheduler.CurrentMicroThread, SynchronizationContext.Current);\n    }\n\n    /// <summary>\n    /// Yields execution until right after the next physics tick\n    /// </summary>\n    /// <returns>Task that will resume next tick.</returns>\n    public TickAwaiter AfterUpdate()\n    {\n        if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null)\n            throw new Exception($\"{nameof(AfterUpdate)} cannot be called out of the micro-thread context.\");\n        return new TickAwaiter(_postTickRunner, Scheduler.CurrentMicroThread, SynchronizationContext.Current);\n    }\n\n    /// <summary>\n    /// Whether a physics test with <paramref name=\"mask\"/> against <paramref name=\"collidable\"/> should be performed or entirely ignored\n    /// </summary>\n    /// <returns>True when it should be performed, false when it should be ignored</returns>","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs#L349-L385","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before (plain thread)\nTask.Run(async () => await sim.NextUpdate());\n// after (inside an async Stride script)\npublic async Task Execute() {\n    var awaiter = sim.NextUpdate(); // running on a micro-thread\n}","handlingStrategy":"validation","validationCode":"if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null) {\n    // not on a micro-thread: do not call NextUpdate()\n    return;\n}\nvar awaiter = sim.NextUpdate();","typeGuard":null,"tryCatchPattern":"try { var a = sim.NextUpdate(); } catch (Exception ex) { log.Warn(\"NextUpdate requires micro-thread context\", ex); /* fall back to event/polling */ }","preventionTips":["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"],"tags":["physics","concurrency","micro-thread"],"backgroundTag":"unsupported-operation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}