Known Limitations
This page is intentionally honest. Every framework has limitations. Knowing them upfront saves hours of frustration.
Compilation
No hot reload. You must rebuild and reinject after every code change. rb watch rebuilds automatically on save, but you still need to reinject manually.
Build order is alphabetical within folders. If a TypeScript component's setup code calls func.SomeHelper and that helper is defined in a .lua file that sorts later in the load order, the call will see nil at setup time. The helper will be available by the time any on.* callback fires (callbacks run during gameplay, well after all files are loaded). Fix: put shared helpers in globals.lua which always loads before components.
No tree-shaking. Every component and helper compiles into the output regardless of whether it's used. For large scripts this increases file size. Not a practical problem for most scripts.
Lua's 200-local limit is handled automatically. Each TypeScript component is compiled into its own isolated function scope, so adding new components never adds to the outer script's local count. Plain .lua helpers accumulate in the outer scope, but there are few of them and they grow slowly.
Signals
Only primitive defaults. signal({}) doesn't work — signals only support boolean, number, and string. Use a plain let variable for tables and other non-primitive state.
No computed/derived signals. const derived = computed(() => signalA() && signalB()) works via the computed() helper, but complex derived state often needs Pulse.Store.watch.
Signal writes are synchronous. Writing a signal fires all subscribers immediately. If a subscriber is slow, it blocks. Don't do heavy work in on.signal(...) handlers — use task.spawn for async work.
UI
No dynamic widget creation. Widgets are created once at load time. You can't add or remove them at runtime.
Linoria must load successfully. If the Linoria UI library fails to load (network issue, executor issue), no UI appears. This is outside Pulse's control.
Multi-select dropdowns use multidropdown(...) widget builder — see Building UI for the full widget list.
Roblox environment
Highlights on replicated objects get destroyed. Parent highlights to CoreGui with Adornee set to the target. See Roblox Gotchas.
workspace.CurrentCamera is nil at inject. Always guard with a load check. See Roblox Gotchas.
Executor feature availability varies. writefile, setclipboard, syn.request — not all executors have all of these. Always pcall executor-specific calls.
Targeting / aimbot
FOV check for large NPCs requires a custom getRoot. Using HumanoidRootPart for a large entity positions the FOV check at the waist, which is often off-screen. Use the nape hitbox instead. See Pulse.Aim.
Pulse.Aim.lookAt depends on VirtualInputManager. Not all executors expose this correctly. Some have partial support. Test on your specific executor.
Dev overlay
FEAT tab shows "not ready" until the script window is opened once. The toggle registry is populated lazily on first window open. Open the main script window once, then FEAT works.
COPY to clipboard may not work on all executors. The overlay tries setclipboard, toclipboard, and writeclipboard in sequence. If none are available, it shows "no clipboard fn". This is an executor limitation.
General
Error messages from Roblox can be cryptic. "attempt to index nil" in a large compiled script gives a line number in the compiled output, not your source file. Use Pulse.Log extensively to narrow down where things break before reading compiled output.
Pulse is not thread-safe. All components run on the same Lua thread. There's no parallel execution. This is normal for Roblox scripts but means heavy loops block everything else.