-
Notifications
You must be signed in to change notification settings - Fork 0
Debugging
A complete debugging guide for the current runtime: how to use ServiceManager, what each tab is for, which console commands exist, where performance data lives, and how to diagnose the most common failures.
Important
This page is based on the current implementations of Logger, Scheduler, BaseEntity, BaseStateMachine, NetworkManager, ServiceManager, ConsoleModule, AnalyticsEngine, and the shipped ServiceManager tests.
For most problems, use this order:
- read the Luau output for boot or runtime errors
- open ServiceManager on a client in Studio
- switch to server context if the bug is authoritative
- inspect the right tab instead of guessing
- use console commands for focused probes
- cross-check profiler and entity/FSM data before changing code
┌────────────────────────┐
│ Something looks wrong │
└────────────┬───────────┘
▼
┌─────────────────────────────────┐
│ Boot failure or runtime failure?│
└──────────────┬─────────────┬────┘
│ Boot │ Runtime
▼ ▼
┌───────────────────────────────────┐ ┌─────────────────────┐
│ Check Studio output + Logger history │ │ Open ServiceManager │
└───────────────────────────────────┘ └──────────┬──────────┘
▼
┌──────────────────────┐
│ What kind of issue? │
└──────┬─────┬────┬────┬──────────────┘
│ │ │ │
│ │ │ └─ need targeted query ─→ [CONSOLE tab]
│ │ └────── lag or budget spike ─→ [TASKS / PROFILER / INSIGHTS]
│ └─────────── network payload weird ─→ [NETWORK tab]
└──────────────── entity data wrong ─────→ [ENTITY tab]
FSM stuck or wrong state ─→ [FSM tab]
[FSM tab] ───────────────┐
[ENTITY tab] ────────────┼─→ [Check transition history + state duration]
[NETWORK tab] ───────────┤
[TASKS / PROFILER / INSIGHTS] ─┤
[CONSOLE tab] ────────────┘
ServiceManager is a client-side tool. The usual development bootstrap is:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Orchestrator = require(ReplicatedStorage:WaitForChild("Orchestrator", 30))
Orchestrator:RegisterComponents()
if RunService:IsStudio() then
local sm = Orchestrator:StartServiceManager()
sm.SwitchSubsystem("FSM")
sm.SetContextMode("server")
end- requires
ServiceManager/init.luau - initializes the GUI, store, behavior tree, analytics engine, navigator, and subsystem modules
- shows the window
- defaults to
TASKSif nothing is selected yet
- it does not replace
RegisterComponents() - it does not boot the framework by itself
- it does not automatically choose the right context for your bug
ServiceManager runs in strict context modes:
clientserver
Switch with either API or console:
sm.SetContextMode("server")or:
mode server
The current implementation does all of the following:
- updates
nexus.modein the reactive store - updates the GUI status bar
- forces a synchronous poll of
ServiceManager_ServerSnapshotwhen entering server mode - drops stale drill-down layers
- pushes a divider into the console output (
context: CLIENT → SERVER) - resets perf-strip smoothing to avoid graph corruption across contexts
The client polls the server snapshot:
- every 2 seconds in server mode
- every 5 seconds in client mode
If you are debugging an authoritative problem and the list looks stale, switch to server mode first.
Best for:
- scheduler health
- verifying the global heartbeat exists
- per-task status (active / idle / archived / completed)
- quick “what is hot right now?” checks
Things to inspect:
GlobalStateMachineHeartbeatServiceManagerBrainTick- execution counts
- average and max run time
- event bucket (
Heartbeat, etc.) - whether a task is recurring
Use TASKS first when:
- FSMs are not updating
- work seems stuck
- performance cost seems task-oriented instead of state-oriented
Best for:
- current state
- transition history
- completed/failed machine inspection
- stall detection
- state graph visualization
Particularly useful fields:
CurrentStateTransitionCountStateDurationTransitionHistoryErrorIsStalled- per-FSM
Perf
The current module also keeps client-side and server-side shadow caches so you can inspect finished machines that have already been unregistered from the live registry.
Best for:
- checking whether the server and client agree on state
- version drift
- replication flags
- schema shape
- whether an entity is still valid
Especially useful for diagnosing:
- “my door opened on the server but not on the client”
-
UpdateEntity()appearing to succeed but visuals not reacting - version gaps or stale updates
Best for:
EntityUpdateEventEntityCommandEventOrchestratorEventOrchestratorFunction- event bus traffic
- server request callbacks
The current implementation keeps structured ArgsStruct style payload snapshots for drill-down, which is much better than raw {table} spam.
Best for:
- persistent entities
- save/load visibility
- checking whether the expected persistence layer is even online
If your issue is “state disappears between sessions”, this is the tab to pair with logs.
Best for:
- anomaly detection
- memory pressure warnings
- stalled FSMs
- slow task bottlenecks
- too many render-priority FSMs
- dead entity backlog
- FPS degradation
The current AnalyticsEngine registers 10 built-in rules, including:
- FSM state stall detection (
> 30s) - high-frequency task bottlenecks (
> 8ms avg) - entity count spike (
> 500) - memory pressure (
> 512 MB) - render-priority overuse (
> 10render-priority FSMs) - orphaned event buses
- failed FSM clusters
- zero-activity scheduler
- dead entity backlog
- frame rate degradation (
< 30 FPS)
Best for:
- focused text queries
- toggling modes
- listing tasks/FSMs/entities quickly
- grabbing a quick perf snapshot without leaving the tab
The console keeps:
- a 500-line output ring buffer
- 100 commands of input history
- autocomplete for commands, subcommands, and dynamic IDs
Best for:
- structured log history
- browsing warnings and errors without relying on the Studio output window alone
- checking whether your own logger messages are appearing at the expected level
Best for:
- frame budget overview
- top hot tasks
- top hot FSMs
- GC and thread-pool diagnostics
- seeing whether the cost is task-side, FSM-side, or memory-side
Think of PROFILER as the aggregation layer over TASKS, FSM, and network telemetry.
The built-in command registry currently includes the following commands.
| Command | What it does |
|---|---|
help |
list commands |
clear |
clear console output |
echo <message> |
echo text back |
version |
show ServiceManager version |
| `mode [client | server]` |
| `tasks [list | info |
| `fsm [list | info |
| `entities [list | info ]` |
network |
summary of network activity |
ds |
summary of persistent entities |
perf |
print memory/FPS/budget metrics |
switch <subsystem> |
jump to another subsystem |
> mode server
Switched to server mode
> tasks list
Scheduled Tasks:
● GlobalStateMachineHeartbeat active (×128)
○ ServiceManagerBrainTick idle (×64)
> fsm info TrafficLight_Demo
FSM: TrafficLightFSM
State: Yellow
Active: true
Priority: 1
Duration: 1.42s
Transitions: 23
> perf
Performance Metrics:
Memory: 118.24 MB
FPS: 60.0
Budget: 15.00ms
- use
mode serverbefore inspecting authoritative data - use
tasks info GlobalStateMachineHeartbeatwhen FSMs appear frozen - use
fsm cancel <id>as a safe recovery tool in development - use
switch profilerwhen you discover a hot task or hot FSM and want the bigger picture
The current FSM implementation exposes or maintains:
State_activeStateNameStateDurationTotalDurationTransitionCountTransitionHistoryPerfIsActive
TransitionHistory entries can include:
FromToTimeArgsWaitSpanContextSnapshotTransitionId
That makes it much easier to answer:
“What state was this machine in, with what context, when it transitioned?”
The current entity implementation tracks:
Version_versionHistoryPending_pendingKeysDataSchemaContextIsValid
_versionHistory is especially valuable when debugging rapid state changes or nil-cleared fields.
Useful scheduler diagnostics include:
Tasks_runningLastFrameStatsTaskStatsPerformanceManager:GetStatsSnapshot()GetThreadPoolStats()
Logger keeps a 500-entry circular buffer. Use it when Studio output has already scrolled past the useful event.
The current Logger supports:
ERRORWARNINFODEBUG
with the current level weights:
ERROR = 1WARN = 2INFO = 3DEBUG = 4
and prints only when the message level is less than or equal to Settings.Logger.MinimumLoggingLevel.
Logger = {
DebuggingEnabled = true,
MinimumLoggingLevel = 4,
}- keep framework
ERRORandWARNvisible - lower noisy
DEBUGlogs unless you are profiling something specific - attach your own failure handlers to FSM lifecycle signals for domain-level alerts
Put logs at:
- state entry for important workflow checkpoints
- failure boundaries
- remote validation rejects
- persistence failures
- unexpected nil/resource-missing situations
Do not spam OnHeartbeat() with per-frame logs unless you are doing a short targeted trace.
Check, in order:
- is the FSM present in the FSM tab?
- is
GlobalStateMachineHeartbeatpresent in TASKS? - is the machine in the correct context (client vs server)?
- is the transition legal according to
validOutcomes? - did
ScheduleTransition()orTimeoutactually get set? - is there an error in
OnLeave/OnEnter/OnHeartbeat?
Use:
fsm info <id>
tasks info GlobalStateMachineHeartbeat
The current BaseEntity:UpdateEntity() fast-fails when:
- the entity is destroyed
- the entity is locked by another caller
- there are no pending changes
- the entity is immutable (
Mutable ~= true)
Inspect the entity in the ENTITY tab and verify the definition itself.
Check:
- is the field marked
Replicate = true? - did the server actually commit the entity with
UpdateEntity()? - does
NETWORKshow the update event? - did the client receive a newer
_v? - is
ApplyChanges()handling the field?
Usually one of three things:
- you forgot
StartServiceManager()on the client - you are in the wrong context
- boot failed before subsystems had real data to fetch
Use this pairing:
-
PROFILERto identify the spike class -
TASKSfor task-level detail -
FSMfor machine-level detail -
INSIGHTSfor rule-based anomaly hints
That is often intentional sanitization. Inspect the detailed network drill-down and remember that deep payloads are bounded to keep the dashboard stable.
Watch the status bar and the console. The client marks nexus.connected = false when ServiceManager_ServerSnapshot polls fail.
ServiceManager also ships with a Studio/test-oriented debug bridge via ReplicatedStorage attributes.
Commands written to ReplicatedStorage:SetAttribute("ServiceManagerCmd", ...) can drive actions such as:
context:serversubsystem:FSMfsmDetail:<id>fsmViz:<id>netDetail:<name>consolePush:<message>
This exists primarily for integration tests and command-bar debugging, but it is useful to know when you need to automate UI inspection.
Before editing code, check:
- correct runtime context selected
-
GlobalStateMachineHeartbeatexists - entity and FSM IDs exist as expected
- replicated field is actually marked
Replicate = true -
Mutable = trueon any entity that commits -
TransitionHistoryor_versionHistoryshow the expected event - profiler data confirms where the cost lives
- console command output agrees with the visual tabs
Quick Links: Home · Quick Start · API Reference · Architecture · Examples · Glossary
Copyright: © 2026 RBXStateMachine contributors · Repository · License information