stride3d/stride · error · Exception
Cannot receive out of micro-thread context.
Error message
Cannot receive out of micro-thread context.
What it means
Channel<T>.Receive() awaits data via a micro-thread awaiter; when no senders are pending it needs the ambient MicroThread.Current to register the receiver for wakeup. If called outside any micro-thread, the library throws, because there is no scheduler to resume the continuation.
Solutions
- Call Receive only inside code running as a micro-thread (e.g. via Scheduler.Spawn/addScript or Script component)
- If you must consume from ordinary code, use a non-blocking check (senders.Count / TryReceive-style polling) or a standard concurrent queue instead
- Wrap the consuming logic in a micro-thread that the scheduler runs
Example fix
// before
Task.Run(() => value = channel.Receive());
// after
Scheduler.Spawn(async mt => { value = await channel.Receive(); }); Defensive patterns
Strategy: type-guard
Type guard
bool canReceive = MicroThread.Current != null;
Try / catch
try { value = await channel.Receive(); }
catch (Exception) when (MicroThread.Current == null) { /* fall back to polling or re-run in micro-thread */ } Prevention
- Only call Channel.Receive inside micro-thread code
- Use polling/queues for cross-context consumption
- Wrap consumers in Scheduler.Spawn micro-threads
When it happens
Trigger: Calling channel.Receive() from a plain Task, thread-pool thread, main thread, or constructor instead of inside a micro-thread started via Scheduler.
Common situations: Calling Receive in unit tests without a scheduler; consuming a Stride channel from a background Task.Run; awaiting a channel in app startup code outside the Stride script/micro-thread system.
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
- MicroThread was already started before.
- MicroThread completed in an invalid state.
- 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/2fbb42bef9738cf5.
Report an issue: GitHub.
Appendix: source
Thrown at sources/core/Stride.Core.MicroThreading/Channel.cs:89
{
receiver.MicroThread.ScheduleContinuation(ScheduleMode.First, receiver.Continuation);
throw new NotImplementedException();
//await Scheduler.Yield();
}
receiver.IsCompleted = true;
return receiver;
}
/// <summary>
/// Receives a value over the channel. If no other <see cref="MicroThread"/> is sending data, the receiver will be blocked.
/// If someone was sending data, which of the sender or receiver continues next depends on <see cref="Preference"/>.
/// </summary>
/// <returns>Awaitable data.</returns>
public ChannelMicroThreadAwaiter<T> Receive()
{
if (senders.Count == 0)
{
var microThread = MicroThread.Current ?? throw new Exception("Cannot receive out of micro-thread context.");
var waitingMicroThread = ChannelMicroThreadAwaiter<T>.New(microThread);
receivers.Enqueue(waitingMicroThread);
return waitingMicroThread;
}
var sender = senders.Dequeue();
if (Preference == ChannelPreference.PreferReceiver)
{
sender.MicroThread.ScheduleContinuation(ScheduleMode.Last, sender.Continuation);
}
else if (Preference == ChannelPreference.PreferSender)
{
sender.MicroThread.ScheduleContinuation(ScheduleMode.First, sender.Continuation);
throw new NotImplementedException();
//await Scheduler.Yield();
}
sender.IsCompleted = true;
return sender;View on GitHub (pinned to 96fad776d2)