{"record":{"id":"83648a04d2c6a63d","repo":"stride3d/stride","slug":"afterupdate-cannot-be-called-out-of-the-micro-thread-context","errorCode":null,"errorMessage":"AfterUpdate cannot be called out of the micro-thread context.","messagePattern":"AfterUpdate 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":378,"sourceCode":"    /// <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>\n    [MethodImpl(MethodImplOptions.AggressiveInlining)]\n    public bool ShouldPerformPhysicsTest(CollisionMask mask, CollidableReference collidable)\n    {\n        var component = GetComponent(collidable);\n        return mask.IsSet(component.CollisionLayer);\n    }\n\n    /// <summary>\n    /// Finds the closest intersection between this ray and shapes in the simulation.\n    /// </summary>\n    /// <param name=\"origin\">The start position for this ray</param>","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs#L360-L396","documentation":"AfterUpdate() yields the current micro-thread until just after the next physics tick, mirroring NextUpdate(). It only works when the caller is on a Stride micro-thread with a SynchronizationContext, since TickAwaiter resumes through those; otherwise it throws immediately.","triggerScenarios":"Calling BepuSimulation.AfterUpdate() from a raw Task, thread pool thread, unit test, or any code where Scheduler.CurrentMicroThread or SynchronizationContext.Current is null.","commonSituations":"Attempting post-physics-step synchronization from background workers or non-Stride test harnesses instead of from within Stride's script/micro-thread scheduling.","solutions":["Invoke AfterUpdate() from an async Stride script running on a micro-thread.","Use engine callbacks/events (e.g. after-tick hooks) for code outside the micro-thread context.","Restructure the logic so the post-tick continuation lives inside the script's async flow."],"exampleFix":"// before\nawait Task.Run(() => sim.AfterUpdate());\n// after\npublic async Task MyScript() {\n    var awaiter = sim.AfterUpdate(); // valid: on micro-thread\n}","handlingStrategy":"validation","validationCode":"if (Scheduler.CurrentMicroThread is null || SynchronizationContext.Current is null) {\n    return; // cannot await AfterUpdate here\n}\nvar awaiter = sim.AfterUpdate();","typeGuard":null,"tryCatchPattern":"try { var a = sim.AfterUpdate(); } catch (Exception ex) { log.Warn(\"AfterUpdate requires micro-thread context\", ex); }","preventionTips":["Keep post-tick continuation logic inside Stride scripts","Avoid calling physics await APIs from unit tests or plain Tasks","Document that AfterUpdate/NextUpdate are micro-thread-only"],"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"}