SignalBus Known Limitations
Review draft. This source page has not completed website editorial and release-specific verification.
SignalBus 1.0.0 is designed as a local, game-thread-only publisher-subscriber system for Unreal Engine.
The limitations below are intentional. Some protect correctness and simplicity, while others represent features that may be considered in future releases.
1. Game-Thread-Only Operation
Section titled “1. Game-Thread-Only Operation”SignalBus publishing, subscribing, unsubscribing, queue processing, diagnostics, and warning analysis must occur on the Unreal game thread.
What this means
Section titled “What this means”You should call SignalBus from:
- Actors
- Actor Components
- Game Instance systems
- World systems
- Blueprint graphs
- Game-thread callbacks
- Replication callbacks such as
OnRep - RPC handlers executing on the game thread
You should not call SignalBus directly from:
- Worker threads
Asynctasks running outside the game thread- Background file-loading callbacks
- Custom thread pools
- External SDK callbacks that execute on another thread
Safe example
Section titled “Safe example”AsyncTask(ENamedThreads::GameThread, [WeakThis](){ if (WeakThis.IsValid()) { // Publish through SignalBus here. }});Unsafe example
Section titled “Unsafe example”Async(EAsyncExecution::ThreadPool, [](){ // Do not publish directly from this worker thread.});Why this limitation exists
Section titled “Why this limitation exists”SignalBus interacts with Unreal objects, reflected payloads, subsystem state, Blueprint callbacks, diagnostics, and editor tooling. Keeping all access on the game thread avoids:
- Race conditions
- UObject lifetime hazards
- Locking complexity
- Non-deterministic callback order
- Thread-unsafe Blueprint execution
2. No Networking or Replication
Section titled “2. No Networking or Replication”SignalBus does not send messages between a server and clients.
A message published on the server remains on the server. A message published on a client remains on that client.
What this means
Section titled “What this means”This does not happen automatically:
Server SignalBus publish -> Client SignalBus subscriberSignalBus must be combined with Unreal networking.
Recommended pattern
Section titled “Recommended pattern”Client input -> Server RPC -> server validates action -> server updates authoritative state -> state replicates to client -> client OnRep publishes local SignalBus message -> local client systems reactServer example
Section titled “Server example”Server applies damage -> server World SignalBus publishes Combat.DamageApplied -> server quest and scoring systems react -> Health replicates normallyClient example
Section titled “Client example”Client receives replicated Health -> OnRep_Health -> client World SignalBus publishes Combat.HealthChanged -> HUD, audio, and effects reactWhy this limitation exists
Section titled “Why this limitation exists”Networking requires:
- Authority rules
- RPC ownership
- Relevancy
- Reliability choices
- Serialization
- Bandwidth controls
- Security validation
Those responsibilities belong to Unreal networking, not a local message bus.
3. No Cross-Process Routing
Section titled “3. No Cross-Process Routing”SignalBus cannot send messages between:
- Separate game processes
- Dedicated server processes and client processes
- Different applications
- Remote tools
- External services
What this means
Section titled “What this means”Even when two processes use the same message ID and payload type, they do not share a bus.
Example
Section titled “Example”Dedicated server process└── local server SignalBus
Client process└── local client SignalBusThe two buses are independent.
Use instead
Section titled “Use instead”Use an appropriate transport such as:
- Unreal replication
- RPCs
- HTTP
- WebSockets
- Sockets
- Online subsystem APIs
- Platform SDK messaging
SignalBus can still be used locally after the external message arrives.
4. Queue Cancellation Is Not Supported
Section titled “4. Queue Cancellation Is Not Supported”Once a queued message is accepted, SignalBus does not expose a public API to cancel it before delivery.
What this means
Section titled “What this means”You cannot currently do this:
Queue message -> receive queue handle -> cancel queued messageRecommended workaround
Section titled “Recommended workaround”Include state in the receiving system so stale work can be ignored.
Example:
USTRUCT()struct FLoadRequestMessage{ GENERATED_BODY()
UPROPERTY() int32 RequestGeneration = 0;};The subscriber compares the generation to the current request and ignores old messages.
Another workaround
Section titled “Another workaround”Use a message that changes state rather than relying on queue cancellation.
Queue Load.StartedQueue Load.CancelledThe subscriber decides whether work should continue.
5. Queue Priority Is Not Supported
Section titled “5. Queue Priority Is Not Supported”All queued messages use FIFO order inside their owning subsystem.
What this means
Section titled “What this means”A later high-importance message cannot automatically jump ahead of earlier queued messages.
Queued first -> delivered firstQueued second -> delivered secondRecommended workaround
Section titled “Recommended workaround”Use separate message IDs or separate higher-level systems for urgent work.
Example:
UI.Notification.NormalUI.Notification.CriticalThe subscriber can prioritize how it handles those categories after receiving them.
Important note
Section titled “Important note”Immediate dispatch is available when something must run now, but it changes callback timing and should not be used only as a substitute for queue priority.
6. Scheduled Delivery Is Not Supported
Section titled “6. Scheduled Delivery Is Not Supported”SignalBus cannot schedule a message for a specific future time.
Unsupported examples
Section titled “Unsupported examples”Deliver in 5 secondsDeliver at 14:00Deliver next frame at a chosen priorityDeliver after three queue passesRecommended workaround
Section titled “Recommended workaround”Use Unreal timing systems:
FTimerManager- Latent Blueprint nodes
- Tick-based scheduler systems
- Gameplay Ability System timers
- Custom task managers
When the timer expires, publish the SignalBus message.
Example:
World->GetTimerManager().SetTimer( TimerHandle, [WeakThis]() { if (WeakThis.IsValid()) { // Publish through SignalBus. } }, 5.0f, false);7. Message Replay Is Not Supported
Section titled “7. Message Replay Is Not Supported”SignalBus does not replay old messages to subscribers that register later.
What this means
Section titled “What this means”Message published -> no subscriber existsSubscriber registers later -> old message is not deliveredSignalBus diagnostics may retain evidence that the message occurred, but diagnostic history is not a replay source.
Recommended pattern
Section titled “Recommended pattern”Store durable state separately.
Example:
Game state stores CurrentObjectiveSignalBus publishes Objective.ChangedA new subscriber should:
- Read
CurrentObjective. - Subscribe for future
Objective.Changedmessages.
Why this matters
Section titled “Why this matters”Events and state serve different purposes:
State = what is true now
Message = something happenedSignalBus is primarily for messages, not durable state storage.
8. Payload Editing Is Not Supported
Section titled “8. Payload Editing Is Not Supported”Payload values displayed in diagnostics or the Inspector are read-only.
You can
Section titled “You can”- Inspect payload metadata
- Inspect captured values when policy allows
- Search diagnostics
- Compare message activity
- Review warnings
- Trace message flow
You cannot
Section titled “You cannot”- Edit a payload in the Inspector
- Resend a modified payload
- Change a queued payload
- Alter a previously dispatched event
- Modify live gameplay state through the Broadcast Graph
Why this limitation exists
Section titled “Why this limitation exists”The Inspector is observational. Allowing payload edits would introduce:
- Unclear authority
- Unsafe object mutation
- Replay semantics
- Editor/runtime coupling
- Difficult debugging side effects
Use normal gameplay or debug commands to change runtime state.
9. No Static C++ or Blueprint Source Scanning
Section titled “9. No Static C++ or Blueprint Source Scanning”SignalBus does not scan project source code or Blueprint graphs to discover every possible publisher and subscriber.
What this means
Section titled “What this means”The Inspector and Broadcast Graph show runtime-observed behavior, not every possible code path.
A publisher appears only after relevant runtime activity is observed.
Example
Section titled “Example”A Blueprint contains a publish node but that branch never executes:
Unused branch└── Publish Signal ImmediateThat publisher may not appear in the Broadcast Graph.
Why this limitation exists
Section titled “Why this limitation exists”Static analysis would require:
- C++ source indexing
- Macro interpretation
- Blueprint asset scanning
- Graph expansion analysis
- Wildcard payload resolution
- Handling dynamically constructed message IDs
- Build-system integration
Runtime observation is simpler and reflects what actually happened.
10. Publisher Graph Nodes Depend on Retained Diagnostics
Section titled “10. Publisher Graph Nodes Depend on Retained Diagnostics”Subscribers are registered, but publishers are not permanently registered.
Publisher nodes in the Broadcast Graph are inferred from observed diagnostic history.
What this means
Section titled “What this means”A publisher may disappear from the graph when:
- Diagnostic history is cleared
- Old events are evicted
- Diagnostics are disabled
- A different context is selected
- The editor session ends
Example
Section titled “Example”Publisher runs once -> appears in graphHistory is cleared -> publisher node may disappearThis does not mean routing is broken. It means the graph no longer has retained evidence of that publisher.
Subscriber difference
Section titled “Subscriber difference”Subscribers can appear through active subscription snapshots because they are explicitly registered.
Publishers are discovered through runtime activity.
11. Broadcast Graph Layout Is Session-Only
Section titled “11. Broadcast Graph Layout Is Session-Only”Broadcast Graph node positions are not saved permanently.
What this means
Section titled “What this means”After closing the editor or reopening the project:
- Manually adjusted positions may reset
- Pan and zoom state may reset
- The graph may rebuild from current runtime observations
During one session
Section titled “During one session”The graph attempts to keep stable node identities and positions across refreshes.
Why this limitation exists
Section titled “Why this limitation exists”Persistent layouts require:
- Per-project storage
- Stable identity across builds
- Rename handling
- Deleted-node cleanup
- User-specific versus shared layout policy
- Source-control considerations
SignalBus 1.0.0 keeps the graph observational and temporary.
12. No Custom Unreal Insights Tab
Section titled “12. No Custom Unreal Insights Tab”SignalBus emits structured Unreal Trace events, but Unreal Insights does not include a dedicated SignalBus tab or timing track.
What is available
Section titled “What is available”- SignalBus trace channel
- Structured runtime trace events
- Real
.utracecapture - Editor-side trace analysis parser
- Automated trace verification
What is not available
Section titled “What is not available”- Dedicated SignalBus Insights window
- Dispatch timeline track
- Subscriber table
- Queue table
- Warning timeline
- Context filtering UI inside Insights
Current workflow
Section titled “Current workflow”Use:
SignalBus Runtime Inspectorfor live debugging.
Use:
SignalBus.Editor.Trace.AnalysisVerificationto verify structured trace capture and parsing.
The standard Unreal Insights Timing window does not automatically display custom SignalBus event rows.
13. Trace Capture Is Verification-Oriented
Section titled “13. Trace Capture Is Verification-Oriented”SignalBus trace support currently focuses on recording structured events and verifying them through an editor-side parser.
Captured information includes
Section titled “Captured information includes”- Context identity
- Scope
- PIE instance
- Message ID
- Payload type
- Subscription handle
- Queue ID
- Dispatch ID
- Dispatch mode
- Callback duration
- Dispatch duration
- Queue delay
- Warning state changes
Not captured
Section titled “Not captured”- Full payload values
- Hidden payload fields
- Arbitrary serialized payload bytes
- Editable runtime state
Privacy behavior
Section titled “Privacy behavior”Payload tracing is metadata-only. Properties marked with SignalBus hidden behavior are not emitted as trace values.
14. Queue Limits Are Hard-Coded in 1.0.0
Section titled “14. Queue Limits Are Hard-Coded in 1.0.0”SignalBus 1.0.0 uses fixed queue limits.
Current limits:
Queue capacity: 10,000 entries per subsystemQueue processing limit: 1,000 entries per passWhat this means
Section titled “What this means”When the queue reaches capacity, further enqueue attempts are rejected until space becomes available.
During processing, SignalBus handles at most the per-pass limit. Remaining entries wait for later passes.
Example
Section titled “Example”10,000 messages queued -> accepted
10,001st message -> rejected as QueueFullProcessing:
Pass 1 -> up to 1,000Pass 2 -> up to 1,000...Why limits exist
Section titled “Why limits exist”They protect the game thread from:
- Unbounded memory growth
- Extremely long queue-processing frames
- Accidental infinite producers
- Runaway Blueprint logic
These values are not currently configurable through project settings.
15. Diagnostic and Warning Thresholds Are Hard-Coded
Section titled “15. Diagnostic and Warning Thresholds Are Hard-Coded”SignalBus 1.0.0 uses fixed limits and thresholds for diagnostics and warnings.
Examples include:
Diagnostic history: up to 2,000 events per subsystemWarning history: up to 1,000 warnings per subsystemPayload snapshot budget: 64 KiBContainer inspection limit: 64 elementsNested payload depth: 8Scalar text limit: 1,024 charactersWhat this means
Section titled “What this means”Older diagnostic or warning entries are evicted as limits are reached.
Large or deeply nested payload snapshots may be truncated.
Example
Section titled “Example”A payload contains an array with 500 elements:
First allowed elements capturedRemaining elements omittedSnapshot marked truncatedThese limits protect editor responsiveness and runtime memory.
They are not configurable through plugin settings in 1.0.0.
16. Blueprint Listener Callbacks Copy Payloads
Section titled “16. Blueprint Listener Callbacks Copy Payloads”Before invoking a Blueprint listener event, SignalBus copies payload data into a safe Blueprint wrapper.
Why this happens
Section titled “Why this happens”Blueprint must not receive a pointer to temporary native dispatch memory.
The copy ensures:
- Payload lifetime is valid during the Blueprint callback
- Queued payload ownership remains safe
- Blueprint does not access freed native memory
- Immediate native routing semantics remain correct
Copying has a runtime cost proportional to payload size.
Small gameplay payloads should be inexpensive.
Large structs, arrays, maps, nested containers, or strings can be more expensive.
Recommended practice
Section titled “Recommended practice”Prefer compact event payloads.
Good:
USTRUCT(BlueprintType)struct FHealthChangedMessage{ GENERATED_BODY()
UPROPERTY(BlueprintReadWrite) float OldHealth = 0.0f;
UPROPERTY(BlueprintReadWrite) float NewHealth = 0.0f;};Avoid treating SignalBus as a bulk-data transport for:
- Large inventories
- Full save-game objects
- Huge arrays
- Large text blobs
- Asset data copies
Pass identifiers or lightweight summaries and let the receiver query durable state where appropriate.
17. FInstancedStruct and StructUtils Compatibility
Section titled “17. FInstancedStruct and StructUtils Compatibility”SignalBus currently uses FInstancedStruct for safe reflected payload ownership.
In UE 5.7, the current StructUtils dependency produces a compatibility or deprecation concern that should be reviewed when Epic provides a confirmed stable replacement path.
Why SignalBus uses it
Section titled “Why SignalBus uses it”FInstancedStruct provides:
- Exact reflected struct identity
- Owned payload memory
- Safe queued payload copies
- Generic payload support
- Blueprint-compatible reflected struct handling
Current status
Section titled “Current status”This is a compatibility concern, not a known runtime failure.
SignalBus 1.0.0 builds and tests successfully with the current implementation.
Upgrade guidance
Section titled “Upgrade guidance”When upgrading Unreal Engine:
- Review Epic’s current
FInstancedStructlocation and module requirements. - Build both runtime and editor targets.
- Run all SignalBus runtime and editor tests.
- Validate queued payload ownership.
- Validate Blueprint payload extraction.
- Validate packaged Development and Shipping builds.
Do not replace the payload-storage layer without confirming equivalent ownership and reflection behavior.
18. Reflected Test Types Remain Compiled
Section titled “18. Reflected Test Types Remain Compiled”Some reflected test payloads or helper types remain compiled because Unreal Header Tool processing does not always allow them to be conditionally removed cleanly.
What this means
Section titled “What this means”These types may exist in compiled modules, but they are inert unless:
- Used by automation
- Explicitly instantiated
- Manually placed in a test map
They do not:
- Publish automatically
- Subscribe automatically
- Run every frame
- Affect production routing
- Add network behavior
- Change packaged gameplay by themselves
Why they remain
Section titled “Why they remain”Reflected Unreal types require stable UHT visibility and generated code. Removing them conditionally can create fragile build behavior.
They remain private validation support rather than public production APIs.
Practical Guidance
Section titled “Practical Guidance”SignalBus is a good fit for
Section titled “SignalBus is a good fit for”- Local gameplay events
- Decoupled UI updates
- World-specific notifications
- Game Instance session events
- Immediate synchronous fan-out
- Deferred FIFO fan-out
- Blueprint and C++ interoperability
- Runtime diagnostics and topology inspection
SignalBus is not intended for
Section titled “SignalBus is not intended for”- Network transport
- Authoritative client requests
- Worker-thread messaging
- Durable event sourcing
- Large data transfer
- Persistent graph authoring
- Arbitrary runtime state editing
- Automatic source-code discovery
Summary Table
Section titled “Summary Table”| Limitation | Practical alternative |
|---|---|
| Game-thread-only | Return to game thread with AsyncTask |
| No replication | Use RPCs or replicated properties |
| No cross-process routing | Use networking, sockets, HTTP, or platform APIs |
| No queue cancellation | Ignore stale messages using IDs or generations |
| No queue priority | Separate message categories or use immediate dispatch carefully |
| No scheduling | Use FTimerManager or another scheduler |
| No replay | Store durable state separately |
| No payload editing | Use normal debug or gameplay commands |
| No static scanning | Use runtime diagnostics and Broadcast Graph |
| Publisher graph depends on history | Keep diagnostics enabled and retain history |
| Session-only graph layout | Reorganize during each session |
| No custom Insights tab | Use Inspector and trace verification test |
| Hard-coded limits | Design within documented limits |
| Blueprint payload copy | Keep Blueprint payloads compact |
| StructUtils concern | Revalidate during engine upgrades |
| Reflected test types compiled | Leave inert unless running tests |