> ## Documentation Index
> Fetch the complete documentation index at: https://docs.seraph.si/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Why a plugin is not loading, running, drawing or saving, and what the log says about it.

Almost everything a plugin does wrong is written to the client log rather than to chat, so that a
misbehaving plugin cannot spam you. Open `logs/latest.log` in your game folder and search for
`[Seraph]`.

## The plugin does nothing at all

There are two switches and both have to be on. Run `/seraph config`, open the **Plugins** tab, and
check the master switch as well as the plugin's own toggle.

While a plugin is disabled it is never run: its listeners are skipped when an event fires, its
commands are not registered and every gated call it makes is refused. Enabling it takes effect at
once, though a plugin that registers things at load time needs a reload with `/seraph plugin`
before you see them.

## The plugin is not in the list

Seraph only discovers `.js` files sitting **directly** in the plugins folder. A file in a subfolder
is not loaded, though a loaded plugin can still `require` one. Check the path:

| Platform | Folder                                                              |
| -------- | ------------------------------------------------------------------- |
| Windows  | `%AppData%\Seraph\plugins`                                          |
| macOS    | `~/Seraph/plugins`                                                  |
| Linux    | `$XDG_DATA_HOME/Seraph/plugins`, or `~/.local/share/Seraph/plugins` |

If the file is in the right place, it threw while loading. The log names the file and the error.

## One call does nothing, the rest works

That call needed a permission the plugin never asked for. The log says so once per plugin and
permission:

```
Plugin 'x' attempted to use 'chat' without being enabled or without declaring that permission.
```

Add it to `permissions` in `declareModule` and reload. Because the warning is printed only once
per permission for the life of the client, restart if you want to see it again.

<Note>
  `console`, `scheduler`, `input`, `text`, `party` and the event listeners themselves are ungated,
  so a plugin that logs happily but cannot send chat is almost always missing `"chat"`.
</Note>

## The plugin stops after a while

Seraph stops a plugin that will not yield rather than letting it hang the client. Loading a plugin
has **ten seconds**, and anything dispatched into it, an event, a command or a timer, has **two**.
Passing the budget stops that plugin and leaves the rest of the client running.

The usual causes are a loop with no exit, a synchronous wait, or heavy work in `tick`,
`renderOverlay` or an anti-cheat check. Move slow work onto a timer, and keep hot callbacks to
arithmetic.

## Edits to a module are ignored

A file named `<name>.mod.js` is a module. It is compiled once and stays loaded for the life of the
client, so neither the folder watcher nor `/seraph plugin` swaps it out; Seraph tells you in chat
that a restart is needed. Ordinary scripts carry on reloading around it.

If you are iterating on something, work in a plain `.js` script and rename it once it settles.

## The plugin is skipped with a reason in the log

Everything in `dependsOn` has to be present and enabled. If one of them is missing or switched off,
the plugin is skipped entirely rather than failing part way through, and the reason is logged.
Plugins that depend on each other in a circle are all still loaded, but the order between them is
arbitrary and the circle is reported.

## A command did not register

Two things stop a command appearing:

* The plugin was disabled when it tried to register, which is logged.
* The name is already taken. Seraph refuses to overwrite an existing command and logs
  `command '/x' already occupies registry space`, so pick another name.

Commands need the `commands` permission, and anything the command prints needs `chat`.

## A listener never fires

Check the spelling first. An unrecognised event name is accepted, but the log warns that nothing
will ever fire it and prints the full list of names that do exist.

If the name is right, the event may not mean what you expect. `playerMove` is the position the
*server* puts you at rather than every step you take, `playerJoin` follows the tab list rather than
the world, and `tick` only fires while you are in a world. The full list is in
[Events](/docs/features/plugin-api/events).

## Nothing is drawn on screen

`renderOverlay` only fires while you are in a world and **no screen is open**, so an overlay is
hidden whenever the inventory, chat or a menu is up. Drawing also needs the `render` permission,
and the coordinates are in scaled screen pixels, so use `render.getScreenWidth()` and
`render.getScreenHeight()` rather than hard-coded numbers.

## Requests start failing

The `network` permission is rate limited to **30 requests per minute per plugin**, counted across
`http.fetch` and `stats`. Past that, calls are refused until the window rolls on.

A chat listener in a busy lobby can pass that in seconds, so match narrowly, or collect what you
want to send and flush it on a timer.

## A cron schedule never runs

`cron` returns `-1` when the expression cannot be read. Check the return value:

```js theme={null}
const id = cron("*/5 * * * *", tick);
if (id === -1) console.error("bad cron expression");
```

It takes the usual five fields, minute, hour, day of month, month and day of week, aligned to the
wall clock rather than to game time.

## Settings do not survive a restart

Writing to `config` changes the live value, but only `savePluginConfig()` writes it to disk:

```js theme={null}
config.greeting = "hello again";
savePluginConfig();
```

`storage` is separate and has its own `save()`. Use `config` for settings the user is meant to
change in the menu and `storage` for data your plugin manages itself.

## The editor does not know the API

`plugins/seraph.d.ts` is rewritten every time plugins load, so load them at least once and make
sure your editor has the plugins folder open as its project root. A `jsconfig.json` beside your
plugins makes it stricter, as described in
[Getting started](/docs/features/plugin-api/basics).

If completion is stale after a Seraph update, reload plugins so the file is regenerated against the
build you are actually running.
