Skip to main content

Testing and debugging

Fix creation errors, understand caching, and avoid script injection.

Debugging loop

1

Preview with sample input

Preview runs the whole template engine but performs no moderation, economy, or database writes — so it is safe to run repeatedly while you iterate.
2

Check what is actually stored

Confirms the saved script, the argument schema raze parsed out of your definition, and which permissions its actions require.
3

Check who can reach it

4

Fix and re-save

;cc set on an existing name overwrites it and keeps a revision, so you can iterate freely.

Creation errors

Add a standalone --, =>, or -> between the definition and the response. It must have whitespace around it.
The name collides with a built-in raze command or with the reserved names cc, customcommand, customcommands. Pick another.
Names allow only lowercase letters, digits, _, and -, up to 32 characters.
Use (name), [name], (name...), or [name...]. Mismatched brackets such as <member] are rejected, and names must start with a letter.
A rest argument swallows the remainder of the input, so it has to be the last declaration.
The script contains an action and you don’t hold the Discord permission it maps to. You cannot create a command that does more than you can.
You’ve hit your tier’s cap on saved commands. Delete one, or upgrade with ;upgrade.

Runtime behaviour to expect


Caching

Custom commands are cached in memory for 30 seconds per server.
  • Editing through ;ccset, remove, enable, disable, allow, unallow, deny, undeny, requireperm, and restrictions clear all clear the cache immediately, so your change is live at once.
  • Editing the database directly — wait up to 30 seconds, or restart the bot.

User input is inert

Everything a member types is neutralised before it reaches the script: braces in argument values are converted to lookalike characters that no tag pattern matches. The same applies to any placeholder whose value is user-controlled text — usernames, nicknames, role names, channel names and topics.
Shout: ❴db.set:secrets:key:value❵
Nothing is written, no action fires, and no output directive is smuggled in. The text is echoed with the braces visibly replaced.
Output directives are read from the saved script before substitution, so {delete} or {silent} typed by an invoker can never take effect either.

Still worth doing

Restrict the powerful ones

A command that bans or writes to the database should still be limited to trusted roles with an allow list.

Watch the allow-list bypass

A user or role allow entry skips the action permission check. Add someone deliberately, not for convenience.

Mind self-assign roles

A top-level {add_role:} applies to whoever runs the command. Only put roles there that anyone reaching the command should be able to hold.

Preview first

;cc preview renders the whole script without performing any action, write, or delivery side effect.