Skip to content
Tuxedo CatSTUDIO
Menu

SignalBus Diagnostics and Warnings

Review draft. This source page has not completed website editorial and release-specific verification.

SignalBus includes two related but separate debugging systems:

Diagnostics
Warnings

Diagnostics record what happened.

Warnings identify activity that may deserve attention.

Both systems are observational only. They do not change routing, cancel messages, remove subscriptions, or repair gameplay logic.


Diagnostics keep a bounded history of SignalBus activity for each World or Game Instance subsystem.

Default capacity:

2,000 events per subsystem

A subsystem means one specific SignalBus context, such as:

World - PIE 0
Game Instance - PIE 0
World - PIE 1
Game Instance - PIE 1

Each context keeps its own history.

Diagnostics may record:

  • Context initialization and teardown
  • Subscription added
  • Subscription removed
  • Expired subscription cleanup
  • Immediate publish start
  • Immediate publish completion
  • Publish rejection
  • Message queued
  • Queue rejection
  • Queued dispatch start
  • Queued dispatch completion
  • Subscriber invocation
  • Subscriber callback duration
  • Dispatch duration
  • Queue delay
  • Payload metadata
  • Publisher identity
  • Subscriber identity
  • Message ID
  • Payload type
  • Dispatch ID
  • Queue Entry ID

Diagnostics answer questions such as:

  • Did the publish happen?
  • Was it accepted?
  • Was it Immediate or Queued?
  • Which subscriber ran?
  • How long did the callback take?
  • Which context handled the message?
  • Did the queue reject the message?
  • Was the payload type correct?

Warnings are higher-level observations produced by warning analysis.

Warnings highlight behavior that may be:

  • Unexpected
  • Inefficient
  • Misconfigured
  • Potentially unsafe
  • Worth investigating

Default retained warning capacity:

1,000 warnings per subsystem

Warnings are enabled by default in non-Shipping builds.

Warnings are disabled by default in Shipping builds.

A warning does not automatically mean SignalBus is broken.

It means SignalBus observed something that may deserve review.


Diagnostics and warnings are separate systems.

Examples:

Diagnostics Enabled
Warning Analysis Disabled

In this case:

  • Event History continues
  • New warnings are not created

Another example:

Diagnostics Disabled
Warning Analysis Enabled

In this case:

  • Retained Event History stops
  • Warning analysis may still react to runtime activity

Clearing one does not clear the other.

Clear History
-> clears diagnostic events only
Clear Warnings
-> clears warnings only

Routing continues in both cases.


Typical defaults:

Diagnostics: Enabled
Warning Analysis: Enabled
Payload Policy: Full

This is useful during development because the Inspector can show detailed event history and payload snapshots.

Typical defaults:

Diagnostics: reduced or policy-controlled
Warning Analysis: Disabled
Payload Policy: Hidden

Shipping behavior protects performance and data exposure.

SignalBus routing remains functional.


Each subsystem keeps a maximum of:

2,000 diagnostic events

When the capacity is exceeded:

  • Oldest events are removed
  • Newest events continue to be recorded

Example:

2,000 events already stored
New event arrives
-> oldest event is evicted
-> new event is added

This prevents unbounded memory growth.

Clearing history removes all retained events for the selected context.

It does not:

  • Remove subscriptions
  • Clear queued messages
  • Clear warnings
  • Stop routing
  • Affect another context

Each subsystem keeps a maximum of:

1,000 retained warnings

When the capacity is exceeded:

  • Old warnings may be evicted
  • New warnings continue to be tracked
  • Deduplication state remains bounded

This prevents warning analysis from growing indefinitely.


Diagnostics can capture a read-only snapshot of payload information.

Payload inspection is separate from routing.

A payload may route successfully even when:

  • Snapshot capture is disabled
  • Snapshot capture is truncated
  • A property is hidden
  • Payload inspection fails

Diagnostics must never change message delivery.


SignalBus supports these payload inspection policies:

Full
SummaryOnly
MetadataOnly
Hidden

Captures supported payload values, subject to limits.

Use when:

  • Debugging wrong values
  • Verifying Blueprint payload extraction
  • Inspecting nested structs
  • Comparing expected and actual message contents

Example:

Payload Type: ST_HealthChanged
Old Health: 100
New Health: 75
Damage Type: Fire

Use Full carefully with large or sensitive payloads.


Captures a compact summary instead of a full property tree.

Use when:

  • You need meaningful context
  • Full payload detail is unnecessary
  • You want lower diagnostic overhead
  • Payloads contain large containers

Example:

ST_InventoryChanged
Slots Changed: 3
Inventory ID: PlayerInventory
Snapshot: Summary

Exact summary behavior depends on the payload inspector implementation.


Captures structural information without payload values.

Typical metadata includes:

  • Payload type
  • Struct size
  • Property count
  • Inspection policy
  • Truncation state

Example:

Payload Type: ST_SaveCompleted
Struct Size: 72 bytes
Property Count: 3

Use MetadataOnly when:

  • Values are sensitive
  • Performance matters
  • Type verification is enough
  • Trace-style analysis is preferred

Suppresses payload values.

Example:

Payload Type: ST_LoginResult
Payload Values: Hidden

Use Hidden when:

  • Data may contain secrets
  • Shipping behavior is being tested
  • You want minimal inspection overhead
  • Payload contents should not appear in tooling

SignalBus limits payload inspection to protect memory and editor responsiveness.

Current limits:

Total snapshot budget: 64 KiB
Nested struct depth: 8
Container elements: 64
Scalar text: 1,024 characters

Maximum captured payload snapshot size:

64 KiB

When the budget is exceeded:

  • The snapshot is truncated
  • Routing still succeeds
  • A payload truncation warning may be created

Example:

Large payload
-> first supported data captured
-> remaining values omitted
-> snapshot marked Truncated

Maximum nested depth:

8

Example:

Root
└── Level 1
└── Level 2
...
└── Level 8

Anything deeper is not expanded.

This protects against deeply recursive or unusually nested payloads.


Maximum captured elements per container:

64

This applies to supported arrays, sets, and maps inside reflected payload structs.

Example:

Array contains 500 entries
-> first 64 inspected
-> remaining entries omitted
-> snapshot marked Truncated

The actual routed payload still contains all 500 entries.

Only diagnostics are limited.


Maximum captured scalar text length:

1,024 characters

Long strings are truncated.

Example:

Original string: 5,000 characters
Captured string: first 1,024 characters plus truncation marker

This prevents large text fields from overwhelming diagnostics.


Properties can be excluded from diagnostic values with:

UPROPERTY(meta = (SignalBusHidden))
FString AuthenticationToken;

A hidden property:

  • Is not shown by value
  • Is not searchable by value
  • Is not traced as a payload value
  • Does not enter Broadcast Graph search text
  • Does not enter warning summaries through payload capture

The Inspector may indicate that the field exists, but it should not expose the contents.

Use SignalBusHidden for:

  • Authentication tokens
  • Private identifiers
  • Sensitive player data
  • Internal secrets
  • Large values that should not be displayed

SignalBus warning analysis may report:

  • No-subscriber publish
  • Duplicate subscription
  • Expired subscription
  • High message rate
  • Slow subscriber callback
  • Slow dispatch
  • Reentrancy
  • Queue backlog
  • Long queue delay
  • Payload truncation
  • Payload capture failure
  • Publish rejection
  • Queue rejection

Each warning is explained below.


A message was published, but no active matching subscriber was found.

Example:

Publisher:
Combat.PlayerDied
Active matching subscribers:
0
  • Subscriber was never created
  • Subscribe code did not run
  • Wrong scope
  • Message ID typo
  • Wrong payload type
  • Listener was destroyed
  • Listener unsubscribed
  • The event is intentionally fire-and-forget

Publisher:

World scope
Message ID: Quest.Advanced
Payload: ST_QuestAdvanced

Subscriber:

Game Instance scope
Message ID: Quest.Advanced
Payload: ST_QuestAdvanced

Result:

No matching World subscriber
  1. Select the correct context.
  2. Open Active Subscriptions.
  3. Search for the Message ID.
  4. Compare scope.
  5. Compare exact payload type.
  6. Confirm the listener is alive.
  7. Check whether Subscribe executed.

Some events are intentionally optional.

Example:

Debug.TelemetryEvent

A no-subscriber warning may be expected if no telemetry listener is active.


The same logical listener appears to have registered more than once for the same contract.

Example:

BP_HUDController
-> subscribes twice to Combat.HealthChanged

One publish may then produce two callbacks.

  • Subscribe called every Tick
  • Subscribe called during repeated widget activation
  • Begin Play path runs more than expected
  • Reinitialization does not unsubscribe first
  • Duplicate components exist
Widget Construct
-> Subscribe
Widget Activated
-> Subscribe again

Result:

One message
-> callback runs twice
  1. Open Active Subscriptions.
  2. Filter by subscriber identity.
  3. Compare Message ID and payload type.
  4. Check subscription handles.
  5. Review the subscriber lifecycle.
  6. Confirm unsubscribe occurs before re-subscribing.

Subscribe once or store and remove the previous handle.


A subscription’s weak lifetime owner no longer exists.

SignalBus skipped or pruned the subscription.

  • Actor was destroyed
  • Component ended play
  • Widget was removed
  • UObject was garbage collected
  • Manual cleanup did not occur before owner destruction
Enemy Actor subscribes
Enemy Actor is destroyed
Later message publishes
-> expired subscription is detected
-> callback is skipped
-> subscription is pruned

Usually no.

The weak owner prevents stale object access.

However, frequent expired-subscription warnings may indicate cleanup should happen earlier.

Unsubscribe during:

EndPlay
Deinitialize
Widget deactivation
Component shutdown

A Message ID is being published very frequently within the warning analysis window.

  • Publishing every Tick
  • Publishing inside a loop
  • Repeated state writes
  • Duplicate publishers
  • Accidental feedback loop
  • High-volume telemetry-like use

Bad pattern:

Tick
-> Publish Combat.HealthChanged

even when Health did not change.

High rates can cause:

  • Callback cost
  • Diagnostic growth
  • Warning noise
  • Queue backlog
  • Editor slowdown
  • Large trace files
  1. Filter Event History by Message ID.
  2. Check publisher identities.
  3. Check event timestamps.
  4. Confirm whether state actually changed.
  5. Look for duplicate publishers.
  6. Check reentrancy warnings.

Publish only when meaningful state changes.


One subscriber callback exceeded the warning threshold.

Message: Inventory.Refreshed
Subscriber: BP_InventoryWidget
Callback duration: 12 ms
  • Expensive Blueprint logic
  • Large loops
  • UI rebuilding
  • Asset loading
  • Synchronous file access
  • Heavy logging
  • Large payload copies
  • Nested publishes
  • Complex queries

Immediate callbacks block the publisher.

One slow callback can make the whole immediate dispatch slow.

  1. Open Warnings.
  2. Note the subscriber identity.
  3. Note the Subscription Handle.
  4. Find the matching dispatch in Event History.
  5. Compare callback durations.
  6. Profile the subscriber logic.
  7. Consider Queued delivery if synchronous completion is unnecessary.
  • Reduce callback work
  • Defer expensive work
  • Cache results
  • Avoid blocking I/O
  • Use smaller payloads
  • Split unrelated reactions

The entire dispatch took longer than the configured threshold.

  • Many subscribers
  • One or more slow callbacks
  • Deep immediate nesting
  • Heavy diagnostics
  • Expired subscription cleanup
  • Complex subscriber-side work
Dispatch ID: 102
Subscribers invoked: 75
Dispatch duration: 22 ms
  1. Find the Dispatch ID.
  2. Review all SubscriberCompleted events.
  3. Compare callback durations.
  4. Count matched and invoked subscribers.
  5. Check for nested immediate dispatch.
  6. Check whether diagnostics or payload inspection are unusually heavy.

A slow dispatch may be caused by one slow subscriber or many small subscribers.


A subscriber published another immediate message while a dispatch was already active.

Reentrancy is supported, but it may create difficult execution chains.

Publish Message A
-> Subscriber A
-> Publish Message B
-> Subscriber B

This can be valid.

A dangerous cycle is:

Message A subscriber publishes Message B
Message B subscriber publishes Message A

This may create:

  • Infinite recursion
  • Stack overflow
  • Repeated state mutation
  • Hard-to-follow ordering
  • Performance spikes
  1. Inspect Dispatch IDs and dispatch depth.
  2. Compare Message IDs.
  3. Identify which callback initiated the nested publish.
  4. Check for A -> B -> A cycles.
  5. Review whether Queued delivery is more appropriate.

Use:

  • State guards
  • Generation counters
  • Queued dispatch
  • Explicit cycle prevention
  • Idempotent subscriber logic

The pending queue has grown beyond a warning threshold.

  • Too many queued publishes
  • Queue processing cannot keep up
  • Publishing every Tick
  • Large bursts
  • Long subscriber callbacks
  • Messages queued during queue processing
  • Paused or non-ticking context
Pending Queue Count: 4,500
Processing Limit Per Pass: 1,000

Several passes are required to drain the queue.

A growing backlog increases:

  • Delivery latency
  • Memory usage
  • Stale event risk
  • QueueFull rejection risk
  1. Open Pending Queue.
  2. Check pending count.
  3. Filter Event History for MessageQueued.
  4. Identify high-volume Message IDs.
  5. Compare enqueue rate with processing rate.
  6. Review subscriber callback durations.
  7. Check queue delay warnings.
  • Reduce publish rate
  • Use Immediate where truly appropriate
  • Coalesce duplicate queued events
  • Replace repeated events with durable state plus one notification
  • Optimize slow subscribers

A queued message waited longer than expected before dispatch.

QueueEntryId: 42
Queue delay: 350 ms
  • Large backlog
  • Context paused
  • Processing limit reached
  • Message queued during active processing
  • Expensive earlier callbacks
  • Very high enqueue volume
  1. Find Queue Entry ID.
  2. Compare enqueue and dispatch times.
  3. Check pending queue count.
  4. Review queue backlog warnings.
  5. Inspect earlier queued messages.
  6. Check whether the World or Game Instance was ticking.

Queue delay does not include callback execution time.


Diagnostic payload capture reached one or more safety limits.

Possible limits:

64 KiB total snapshot
Depth 8
64 container elements
1,024-character scalar text
Payload array contains 500 elements
Only 64 captured
Snapshot marked Truncated

The routed payload is not truncated.

Only the diagnostic snapshot is truncated.

Usually none is required.

For easier diagnostics:

  • Use smaller payloads
  • Use MetadataOnly
  • Pass identifiers instead of bulk state
  • Avoid large nested containers

SignalBus could not fully inspect the payload for diagnostics.

  • Unsupported reflected property type
  • Invalid reflected data
  • Unexpected property layout
  • Capture budget failure
  • Engine compatibility issue
  • Diagnostic traversal edge case

Routing should still continue.

Payload inspection failure must not block valid delivery.

  1. Check payload type.
  2. Review unsupported fields.
  3. Try MetadataOnly.
  4. Reproduce with a smaller payload.
  5. Check engine-version compatibility.
  6. Run payload inspection automation tests.

An immediate publish was rejected before dispatch.

Possible reasons include:

  • Invalid Message ID
  • Invalid payload type
  • Invalid context
  • Unsupported payload
  • Wrong thread
  • Subsystem unavailable
FSignalBusMessageId InvalidId;
Bus->PublishImmediate(InvalidId, Payload, this);

Result:

Publish rejected
Reason: Invalid Message ID
  1. Review failure reason.
  2. Confirm Message ID is not None.
  3. Confirm payload is a reflected struct.
  4. Confirm valid World or Game Instance.
  5. Confirm game-thread execution.
  6. Confirm subsystem exists.

A queued publish was rejected.

Possible reasons include:

  • Invalid Message ID
  • Invalid payload
  • Invalid context
  • Queue full
  • Payload copy failure
  • Wrong thread
  • Subsystem unavailable

Current capacity:

10,000 messages per subsystem

When full:

10,001st enqueue
-> rejected
  1. Review rejection reason.
  2. Check Pending Queue.
  3. Check queue backlog warnings.
  4. Confirm payload can be copied.
  5. Confirm Message ID and context.
  6. Reduce enqueue rate.

Warnings may use severity levels such as:

Info
Warning
Error

Exact severity names depend on the implementation.

General interpretation:

Notable behavior that may be normal.

Example:

Expired subscription pruned

Potential design or performance issue.

Example:

High message rate
Slow callback
Queue backlog

A serious rejection or invariant problem.

Example:

Invalid publish contract
Unexpected payload inspection failure

Severity does not automatically change runtime behavior.


Repeated occurrences may update one warning instead of creating a new row every time.

Example:

No Subscriber warning
Occurrence Count: 25

This reduces warning spam.

A warning may track:

  • First occurrence
  • Last occurrence
  • Occurrence count
  • Resolved state
  • Related Message ID
  • Related Dispatch ID
  • Related Queue Entry ID

Some warnings can become resolved when the condition stops.

Example:

Queue backlog detected
Queue later drains
Warning marked Resolved

Resolved warnings may remain visible for historical context.

Clearing warnings removes retained warning records.

It does not disable future warning creation.


Use this general workflow.

  1. Open:
Tools → Director Suite → Runtime Inspectors → SignalBus Runtime Inspector
  1. Select the correct World or Game Instance context.
  2. Open the Warnings panel.
  3. Select the warning.
  4. Record:
    • Warning type
    • Severity
    • Message ID
    • Subscriber identity
    • Dispatch ID
    • Queue Entry ID
    • Occurrence count
  5. Open Event History.
  6. Filter by the same Message ID or ID fields.
  7. Review the related publish, queue, dispatch, and callback events.
  8. Open Active Subscriptions if the warning involves a listener.
  9. Open Pending Queue if the warning involves queue delay or backlog.
  10. Reproduce the issue with history cleared.
  11. Fix the gameplay or system logic.
  12. Re-run and confirm the warning no longer occurs or resolves.

Warning:

Type: No Subscriber
Message ID: Quest.Advanced
Scope: World

Steps:

  1. Select the World context.
  2. Open Active Subscriptions.
  3. Search for Quest.Advanced.
  4. Confirm expected payload type.
  5. If missing, inspect listener Begin Play.
  6. Confirm Subscribe ran.
  7. Confirm listener uses World scope.
  8. Trigger publish again.
  9. Confirm warning no longer increments.

Warning:

Type: Slow Subscriber
Message ID: Inventory.Refreshed
Subscriber: BP_InventoryWidget
Duration: 18 ms

Steps:

  1. Open Event History.
  2. Find the Dispatch ID.
  3. Find SubscriberCompleted for the widget.
  4. Confirm callback duration.
  5. Profile the widget event.
  6. Remove unnecessary loops or rebuilds.
  7. Consider Queued delivery only if timing allows.
  8. Trigger again.
  9. Confirm duration decreases.

Warning:

Type: Queue Backlog
Pending Count: 6,200

Steps:

  1. Open Pending Queue.
  2. Sort or filter by Message ID.
  3. Identify the largest producer.
  4. Check whether it publishes every Tick.
  5. Check whether several identical events can be coalesced.
  6. Review slow subscriber warnings.
  7. Reduce enqueue rate.
  8. Run again.
  9. Confirm pending count drains.

Clears diagnostics for the selected context.

It does not:

  • Clear warnings
  • Clear queue
  • Remove subscriptions
  • Disable diagnostics
  • Affect another context

Clears warning records for the selected context.

It does not:

  • Clear diagnostics
  • Fix the condition
  • Disable warning analysis
  • Affect another context

A cleared warning may return when the condition happens again.


Diagnostics and warnings have runtime cost.

Cost may come from:

  • Event creation
  • Identity string capture
  • Payload inspection
  • Warning analysis
  • History retention
  • Editor refresh

To reduce overhead:

  • Use MetadataOnly or Hidden when values are unnecessary
  • Disable diagnostics during performance-sensitive testing
  • Disable warning analysis when not needed
  • Avoid publishing every Tick
  • Keep payloads compact
  • Clear history before reproducing one issue
  • Inspect one context at a time

Tracing remains separate and may still be enabled when diagnostics are disabled.


Not always.

Examples:

Slow callback
High message rate
Payload truncation

Delivery may still have succeeded.

No.

It only removes the retained record.

No.

Routing continues.

No.

Only the diagnostic snapshot is truncated.

No.

Some messages are intentionally optional.

Not necessarily.

Duplicate subscriptions are supported, but they should be intentional.


During normal development:

Diagnostics: Enabled
Warning Analysis: Enabled
Payload Policy: Full or SummaryOnly

During high-volume performance testing:

Diagnostics: Disabled or MetadataOnly
Warning Analysis: Enabled selectively
Tracing: Enabled when needed

During sensitive-data testing:

Payload Policy: Hidden
SignalBusHidden: applied to sensitive fields

Before release:

Review all persistent warnings
Confirm no accidental duplicate subscriptions
Confirm no high-rate Tick publishers
Confirm queue backlog remains controlled
Confirm slow callbacks are understood
Confirm payload privacy rules

Warning Usually means First place to check
No Subscriber No active exact match Active Subscriptions
Duplicate Subscription Same listener registered repeatedly Listener lifecycle
Expired Subscription Owner was destroyed EndPlay cleanup
High Message Rate Message published too often Event History
Slow Callback One listener is expensive Subscriber timing
Slow Dispatch Whole fan-out is expensive Dispatch details
Reentrancy Nested immediate publish Dispatch depth
Queue Backlog Enqueue rate exceeds processing Pending Queue
Long Queue Delay Message waited too long Queue Entry ID
Payload Truncated Diagnostic limits reached Payload Viewer
Payload Capture Failure Inspector could not inspect fully Payload type
Publish Rejected Invalid immediate publish Failure reason
Queue Rejected Invalid enqueue or queue full Queue result

Diagnostics tell you:

What happened

Warnings tell you:

What may need attention

The recommended troubleshooting order is:

1. Select the correct context
2. Read the warning
3. Match Message ID and IDs
4. Inspect Event History
5. Inspect Active Subscriptions
6. Inspect Pending Queue
7. Reproduce with cleared history
8. Fix the underlying logic
9. Confirm the warning stops or resolves