Troubleshooting -- Common Issues & Solutions¶
Back to Index | FAQ | Getting Started | Glossary
Not receiving messages¶
Use the Editor tools to locate the failed stage before changing code:
- Enable Editor diagnostics and open Message Monitor. If the emission is absent, inspect the sender, route kind, and target/source context.
- Select the emission to inspect its exact message type and context. Enable stack-trace capture only when you need the emitting file and line.
- If the receiver uses a loaded-scene
MessagingComponent, open Flow Graph. A missing component edge points to registration, token state, or provider setup. An existing edge with no calls points to context mismatch, an interceptor veto, or disabled state. Direct bus or token registrations outside those components do not appear in the graph; use bus logs and registration counters for those paths. - Check the Inspector overlay for missing
MessageAwareComponentbase calls and provider warnings.
Then check the matching cause below:
- Ensure your
MessageRegistrationTokenis enabled (Enable inOnEnable, Disable inOnDisable). - Verify the category matches the emission (Untargeted vs Targeted vs Broadcast).
- Targeted/Broadcast require a valid
InstanceId; ensure the target/source object exists when you emit. - In Unity, confirm your
MessagingComponentexists on sender/receiver GameObjects. - CRITICAL: If inheriting from
MessageAwareComponent, ensure your overrides call base methods: base.RegisterMessageHandlers()- Call this FIRST in your override to preserve parent class registrations. OverrideRegisterForStringMessages => trueseparately when you want the built-in string demos.base.Awake()- Call this if you overrideAwake(), or your token won't be created (this is the #1 cause of handlers not firing).base.OnEnable()/base.OnDisable()- Call these so the token actually enables/disables.base.OnDestroy()- Call this if you overrideOnDestroy(), or registrations leak past the component's lifetime and held references prevent GC.- Never use
newto hide Unity methods (e.g.,new void OnEnable()); always useoverrideand callbase.*. - For the complete table of guarded methods and the exact failure mode for each, see Inheritance and base calls in the quickstart.
Registration timing¶
- ALWAYS register message handlers in
Awake(), notStart(). MessageAwareComponentautomatically callsRegisterMessageHandlers()inAwake().- Registering in
Awake()ensures handlers are ready before other components'Start()methods run. - If you register in
Start(), you may miss messages emitted by other components in theirStart()methods.
Unexpected ordering¶
- Check
priorityvalues on registrations; lower runs earlier. Same priority is registration order. - Interceptors always precede handlers and can cancel; confirm interceptors return
true.
Double registration or over-deregistration warnings¶
- Avoid calling stage/enable multiple times; pair registrations and lifecycles consistently.
- Use Flow Graph to inspect loaded-scene
MessagingComponentroute topology. Review logs withbus.Log.Enabled = truefor direct registrations or when you need the registration mutation history.
Allocations and boxing¶
- Prefer struct messages implementing the generic interfaces:
I*Message<T>. - Use readonly by-reference handler overloads to avoid copies.
- Register handlers once in
Awake/setup, not every frame: each registration allocates a small bounded amount, while ordinary typed steady-state struct dispatch is allocation-free. Struct global accept-all dispatch boxes the message, and emission-site stack-trace capture allocates while enabled. See the allocation FAQ.
Emitting while disabled¶
- If you need to emit when a component is disabled, use a bus not tied to enable state or set
emitMessagesWhenDisabledonMessagingComponent.
Diagnostics overhead¶
- Diagnostics are off by default. Leave them off in release builds (
IMessageBus.GlobalDiagnosticsTargets = DiagnosticsTarget.Off); enable them only when inspecting message history. See Diagnostics.
Source generator did not generate Emit or handler methods¶
- Mark the message type
partial. The generator emits members into a second partial declaration, which requires the keyword on your type. - Apply a
[DxUntargetedMessage],[DxTargetedMessage], or[DxBroadcastMessage]attribute, or implement the matchingI*Messageinterface directly. - An assembly definition is not required -- generation runs for Unity's default
Assembly-CSharpas well as for your own.asmdefassemblies. - After changing message types, let Unity finish recompiling; generated members appear once the analyzer reruns.
Memory grows in long sessions¶
- Read
bus.OccupiedTypeSlotsandbus.OccupiedTargetSlots(or the globalMessageHandler.MessageBus.OccupiedTypeSlots/OccupiedTargetSlots) at region boundaries to see whether per-type or per-target slots are the culprit. - Call
MessageHandler.TrimAll(force: true)(orbus.Trim(force: true)) at scene unload or other natural transitions. Slots that survive a forced trim correspond to active registrations. - Tune the reclamation policy through
DxMessagingRuntimeSettings. See the Memory Reclamation guide for tuning recommendations and a leak-watching pattern.
Related Documentation¶
- Get Unstuck
- to FAQ -- Common questions answered
- to Getting Started -- Learn the basics
- to Glossary -- Understand the terminology
- Debug & Inspect
- to Diagnostics -- Inspector tools and debugging
- to Listening Patterns -- Verify you're listening correctly
- to Message Types -- Ensure you're using the right type
- Examples
- to Mini Combat sample -- See working code
- to Common Patterns -- Real-world solutions