FAQ & troubleshooting
General
Where do my commands actually live?
For most commands: as ModuleScripts inside ServerStorage.CommandRunnerScripts. Each command has a sibling Types ModuleScript next to it.
For @client-tagged commands: the canonical lives in ReplicatedStorage.CommandRunnerRuntime (when @client only) or in both places, ServerStorage canonical + ReplicatedStorage mirror, when @server is also set. See tags & runtime delivery.
Are my commands shared with other people on the team?
No. Commands live inside the place file (the .rbxl / .rbxlx). If you want a command available to everyone on the team, it has to be in the place. Use the cross-place personal library to copy a command from one place to another for your sessions; collaborators will need their own copy.
Can I version-control my commands?
Yes. The ModuleScripts are real Instances inside ServerStorage. Use Rojo (or any sync tool) to mirror them to disk. CommandRunner doesn't care where the scripts came from, it works against whatever's currently in ServerStorage.CommandRunnerScripts.
Authoring
My Run function isn't being called when I press Execute.
Check the output console. CommandRunner catches errors and surfaces them as warn calls plus a toast notification, most likely your script has a syntax error or is throwing during require.
If there's no error, make sure:
- The module returns a table with a
Run = function(props) ... endfield. Runis afunction, not a string ornil.- You haven't accidentally typed
Run = function() ... end(). That calls the function and assigns the result.
My per-argument Run callback isn't firing.
Per-argument callbacks fire on commit, which means:
- For
stringandparagraph:FocusLost(Tab out, Enter, click elsewhere). Typing alone doesn't fire it. - For
number:FocusLost, OR a stepper-arrow click. - For
boolean/switch: every click. - For
button: every click. - For
dropdown: when an option is picked. - For
instance: when the picker button is clicked.
If you're typing in a field and waiting for the callback during typing, that's why. Commit first.
How do I get rid of an argument I no longer use?
Delete it from the Arguments table in the schema and save. CommandRunner sweeps the corresponding card, the persisted Roblox attribute, and any __InstanceArg_* ObjectValue child on the next reconcile.
Can I have nested or grouped arguments?
Not yet. The args panel is a flat list. Workarounds:
- Use
LayoutOrderto group related arguments visually. - Use a
labelargument as a "section header" between groups. - Hide rarely-used arguments (right-click → Hide Argument) so the visible list stays focused.
Runtime delivery
My @client command works in plugin context but not at runtime.
A few things to check:
- Did you actually tag it
@client? The string must be"@client"exactly, in theTagsarray. - Did you save the script via the toolbar's edit-pencil → save? Editing
Tags = {...}and pressing Studio'sCtrl+Sdoesn't trigger the routing reconcile. Use the toolbar save flow (or toggle the widget) to apply. - Are you
require-ing the right path? The script lives atReplicatedStorage.CommandRunnerRuntime.<commandName>. Use:WaitForChildif your consumer runs early. - Did you build a
propsstand-in? The plugin'spropsonly exists in the plugin context. At runtime, you construct your own. See the client runtime delivery recipe.
"Save edits" on stop play test loses my changes for an @client + @server command.
Yes, that's expected behavior. With both tags, the ServerStorage copy is the canonical and the ReplicatedStorage copy is a mirror. When you toggle the widget back on, the plugin re-clones canonical → mirror, overwriting your in-play edits.
Edit the ServerStorage canonical instead. Or, if you don't need server-side ergonomics, drop @server from the tag list. @client-only commands survive play-test edits.
Plugin behavior
My settings reset after I uninstall and reinstall the plugin.
plugin:GetSetting/SetSetting is namespaced per-plugin per-user. Uninstalling clears the namespace. Reinstalling gets a clean slate.
The cross-place library (Library_v1 setting) is the one place this matters most. There's no built-in "export library" feature in the current build. Back up by selecting the relevant entries' source code into your own files before uninstalling.
The widget is empty after a Studio update / plugin reinstall.
Confirm ServerStorage.CommandRunnerScripts exists in the Explorer. If it doesn't, toggle the widget off and on. CommandRunner creates it if missing. If it exists but is empty, your scripts may have lived in a different place file.
Notifications won't go away.
Check the toast's Timeout. Timeout = 0 means sticky. The toast stays until the user clicks an action button. If your code passes 0 and provides no buttons, the toast has no way to dismiss.
For sticky toasts without buttons, set a finite Timeout (e.g. 30) or always provide at least a Cancel button.
Debugging
How do I see what code is actually running?
Set Debug = true on the command:
return {
Debug = true,
-- ...
}
Save. CommandRunner will clone the module to ServerStorage.CommandRunnerDebug under a name suffixed " - DEBUG SCRIPT CHANGES DONT SAVE" on every Execute. Open that clone in the script editor to see exactly what was loaded. Useful when you suspect stale source or caching weirdness.
Don't ship with Debug = true. The clone is regenerated every Execute, so any edits to it are thrown away.
My command is slow.
A few common causes:
- Walking
:GetDescendants()on a huge selection. - Setting many properties one at a time on many parts (Roblox's property pipeline doesn't batch; that's just the cost).
- Sending many notifications inside a loop.
Profile with os.clock() and print. Same as any Lua perf work. CommandRunner doesn't add measurable overhead beyond opening/closing one ChangeHistoryService recording.
Anything else?
Open an issue or ping the support channel listed on the marketplace page. If you've found a bug, include:
- Your CommandRunner version (visible in the changelog; match against current behavior).
- A minimal reproduction command (paste the source).
- The console output, if any errors appeared.