Troubleshooting#
Start with /vg status. It reports the backend, schema version, DAO health, queue
depth, drop counters, hook chain, and permission mode — most problems are visible
there before you reach for a log.
The command does not exist#
Symptom: /vg is not in tab-completion, or the server says
Unknown or incomplete command.
Check the startup log for two lines. The first confirms the mod loaded:
[VonixGuardian]: VonixGuardian (NeoForge 1.21.1) loading.
The second confirms the engine booted and the command tree registered:
[VonixGuardian]: VonixGuardian bootstrap complete.
If the load line is missing, the jar is not being loaded at all — check it is in
mods/, matches your loader and Minecraft version, and that no second
VonixGuardian jar is present.
If the load line appears but bootstrap complete. does not, the engine failed to
boot. The cause is logged between those lines; the common ones are a config that
fails validation (the message lists every problem) or a database that cannot be
opened. Since 2.2.1 a boot failure also logs an explicit second error stating that
/vg is not registered:
[VonixGuardian]: Failed to boot VonixGuardian
[VonixGuardian]: /vg is NOT registered on this server — see the boot error above.
If both lines appear but players cannot see the command, it is a permission problem, not an install problem — see Permissions.
The config is rejected#
Symptom: boot fails with
GuardianConfig validation failed (3 problems):
- lookup.defaultPageSize: must be in [1,50] (got 0)
- purge.autoPurgeSeconds: must be 0 (unbounded) or >= 2592000 (30 days) (got 86400)
- theme: must be one of [aqua, blue, gold, green, purple, red, white] (got dark)
Validation is all-or-nothing and it lists every problem, not just the first. Fix them all at once and restart. The server runs with the previous values until the whole file is valid.
The most common causes:
| Cause | Fix |
|---|---|
| Copied a config from a modpack with a different schema | Delete config.json and let it regenerate with defaults. |
| JSON syntax error | The loader strips // and # comments and tolerates trailing commas, but not missing braces or quotes. Validate with python3 -m json.tool config.json. |
hashIps: true with the placeholder salt |
Set a real salt of at least 16 characters, or set hashIps: false. |
| A value typed as a string | "maxSize": "50000" is rejected; use 50000. |
Queue depth keeps climbing#
Symptom: /vg status shows a rising depth and eventually dropped above zero.
This means the writer cannot keep up with event volume. Diagnose in this order:
- Is the backend healthy?
/vg status→health UNHEALTHYmeans the DAO cannot reach the database. Fix connectivity first; queue depth is a symptom. - Is the server genuinely flooding? Read the
by-typehistogram. One action type dominating means a specific pipeline is producing too much — usuallyentityblockon a modded pack. - Is storage slow? SQLite on a network share or a spinning disk will not keep up with a busy modded server. Move to MySQL or PostgreSQL.
Each has a specific remedy:
| Finding | Action |
|---|---|
entityblock dominates |
Add the modded mobs you care about to entityChangeAllowlist instead of leaving entityChangeLogAllEntities on. Check the coalescer is active in /vg status. |
block dominates from fluid |
Ensure logWaterFlow and logLavaFlow are false (the defaults). |
block dominates from mining |
Add minecraft:stone, minecraft:dirt, minecraft:cobblestone to blockBlacklist. |
chat or command dominates |
These are high volume on a large server. Disable with actions.logChat / logCommands if you do not need them. |
| Nothing dominates but depth climbs | Storage is the bottleneck. Raise queue.batchSize, then consider a remote backend. |
| A sudden spike during a lag event | Engage the kill switch: actions.mixinHotEvents: false stops hot-tick mixin events reaching the queue. Re-enable it afterwards. |
You can stop the bleeding immediately:
/vg consumer pause
Events buffer instead of being written. This does not fix anything — it trades
database pressure for memory, and the buffer still fills. Use it to stabilise while
you diagnose, then resume.
For the queue-fill mechanics see Database & storage → Quarantine and drops.
Rollback reports zero affected actions#
Symptom: the rollback succeeds but nothing changed.
[VonixGuardian] Rollback SUCCESS affected=0 planned=0 skipped=0 ...
[VonixGuardian] Rollback found 0 matching actions. Explosions record their blast
CENTER — try r:20+ or move to the source of the damage.
Work through these in order:
- Radius.
/vg rollbackdefaults tor:10centred on you. If the damage is further away, it is not in scope. User:#globalor move closer. - Time. Without
t:, the query is unbounded — so this is rarely the cause. Witht:1h, verify the damage actually happened in the last hour. - World. Lookups and rollbacks default to the world you are standing in. If
the damage is in the Nether and you are in the Overworld, add
r:#world_the_nether. - Action filter.
a:blockcovers both directions;a:+blockis place only,a:-blockbreak only. A filter that excludes the action you want returns nothing. - Explosions. Explosion rows record the blast centre, not each destroyed
block. A blast whose centre is outside your radius is missed. Widen
r:or roll back from the blast site.
Confirm you are matching rows before rolling back at all:
/vg lookup u:Griefer t:1h r:50 #count
If that count is zero, the rollback had nothing to do. Fix the lookup, not the rollback.
Rollback says PARTIAL or REPAIR_REQUIRED#
Symptom: result status is not SUCCESS.
[VonixGuardian] Rollback PARTIAL affected=310 planned=320 skipped=2 failed=8
compensated=0 repairRequired=8 batchId=19 closed=false
| Status | What happened | What to do |
|---|---|---|
PARTIAL |
Some steps applied, some skipped or failed. | Read the counts. Skipped rows are usually protected by a blacklist or already in the target state. |
COMPENSATED |
A failure was rolled forward and the world was restored to its pre-mutation state. | The rollback did not take effect. Investigate the log for the cause, then retry. |
REPAIR_REQUIRED |
A step neither applied nor compensated. | The rows are recorded in vg_repair_required. Inspect them with /vg lookup before re-running. |
FAILED |
Nothing applied. | Check the log for the exception; a failed rollback leaves the world untouched. |
repairRequired rows are listed by /vg status:
§ Rollback recovery
repair required 8
repair action 4412 batch=19 reason=target block entity type mismatch
There is no automatic repair path. Inspect the position, decide whether the block state is correct, and re-run the rollback for that region if needed. A blind retry of the whole batch after a partial application can double-apply steps.
An open batch is listed in status#
Symptom: /vg status shows an open rollback batch after a restart or crash.
§ Rollback recovery
open batches 1 — inspect with /vg lookup before re-running rollback or restore
batch 17 mode=ROLLBACK affected=318
This is expected behaviour, not a bug. Boot never replays an open batch — doing so would apply a half-finished rollback a second time.
Recovery is manual:
/vg lookupthe batch's affected region to see current world state.- Decide whether the rollback completed, partially completed, or did not run.
- Re-run it explicitly, or leave it.
Players can see everything, or nothing#
Symptom: a staff member's lookups return other players' actions when they
should not, or a player with lookup sees nothing.
Permission scoping is per-category. A player holding vonixguardian.lookup but not
vonixguardian.lookup.chat has chat rows filtered out of results — and out of
#count — silently.
To diagnose, compare what the player sees with what the console sees for the same filter:
/vg lookup r:20 t:1h #count ← as the player
/vg lookup r:20 t:1h #count ← from console
Differing counts mean child-node filtering is active. Grant the specific child nodes the player needs, or grant the family node plus the children.
There is no "self-scoped" default in this release: a player with lookup sees all
rows their child nodes permit, not only their own. If you need players to audit
only their own builds, the current mechanism is per-category scoping — grant
lookup.block and not lookup.container, and tell them to filter with u:<self>.
Permission denied with the right LuckPerms node#
Symptom: /lp user X permission check vonixguardian.lookup returns true, but
the command is refused.
Three causes, in order of likelihood:
- The node name has the wrong shape.
config,teleport, andgivearevonixguardian.command.<verb>; everything else isvonixguardian.<verb>. A plausible-lookingvonixguardian.teleportgrants nothing. vonixguardian.baseis missing. It gates the root/vgliteral. Grant it along with any other node.- LuckPerms is not detected. Check
/vg status→Permissions. Ifpermissions.useLuckPermsistruebut LP's not on the classpath, the resolver falls back to op level and the node never gets consulted.
If a permission change does not take effect after /lp, remember that
permissions.* is restart-only in VonixGuardian — but that affects the config
block, not LP's own live checks. An LP group change applies on LP's next recalculation.
A modded mob's griefing is not logged#
Symptom: a dragon or other modded creature destroys blocks and no rows appear.
This is the shipped default, not a bug. On Forge and NeoForge,
LivingDestroyBlockEvent is a prospective permission query fired per mob tick —
a flying dragon can fire it hundreds of thousands of times per second without
destroying anything. Recording all of them would flood the queue.
Vanilla mobs known to change blocks (enderman, ender dragon, wither, ravager, silverfish, turtle, fox, zombie, falling block) are recorded automatically. Everything else must be opted in:
/vg entitylog add isleofberk:night_fury
/vg entitylog add isleofberk:*
/vg entitylog list
Verify the allowlist loaded — /vg status does not list its contents, but the
command reports the count, and /vg entitylog list prints the entries.
Do not reach for entityChangeLogAllEntities: true. It records everything and
re-opens the flood. See
Configuration → Modded entity griefing.
The database file vanished or is corrupt#
Symptom: /vg status reports health UNHEALTHY, or boot fails opening the
database.
Check the file exists and is not zero-length:
ls -l vonixguardian.db*
A zero-length file with a -wal sidecar of non-zero size means the WAL holds the
data but the main file was truncated — usually a disk-full or an unclean shutdown.
Recovery order:
- Restore from backup. This is the only reliable path. See Database & storage → Backup and restore.
- If there is no backup, try SQLite's own recovery before deleting anything:
sqlite3 vonixguardian.db "PRAGMA integrity_check;"
sqlite3 vonixguardian.db ".recover" | sqlite3 vonixguardian-recovered.db
- Only then start fresh by moving the broken files aside:
mv vonixguardian.db vonixguardian.db.broken
mv vonixguardian.db-wal vonixguardian.db-wal.broken 2>/dev/null
A fresh database is created on next boot. You lose history, not the world.
Permission errors look like corruption
If the server process cannot write to the directory, SQLite fails to open rather than reporting a permission problem directly. Check ownership and mode on the server root before assuming the file is damaged.
/vg reload says "Requires restart" for a value you changed#
That is intentional. Only a subset of config is hot-swappable. database.*,
queue.*, permissions.*, lookup.maxConcurrent, privacy.salt, and the
logFile directory/rotation settings need a restart, and the reload reports them
by name rather than pretending to apply them.
The full matrix is on Configuration → Reload vs restart.
Lookup aborted at the 100000-row scan limit#
Symptom:
[VonixGuardian] Lookup aborted: the permission-filtered page exceeded the
100000-row scan limit. Narrow the time, radius, or action filter and try again.
This happens when your permission child nodes deny most action types: to fill one page of visible rows, the query may have to scan past many denied rows. It fails closed rather than paging through an unbounded scan.
Fix by narrowing the query (t:1h instead of unbounded, r:50 instead of global,
an a: filter) or by granting the child nodes that are being filtered out. The
limit is not configurable.
Command worker saturated#
Symptom:
[VonixGuardian] Command worker is saturated; retry shortly or reduce concurrent
heavy commands.
Several staff ran heavy queries or rollbacks at once and the bounded command worker pool — two threads with a 64-task queue — is full. The rejection is deliberate: an unbounded pool would exhaust memory instead.
Wait a moment and retry, or stagger the work. If this happens routinely, the queries
are too broad — a r:#global lookup over an unbounded time window on a large catalog
is the usual culprit.
Getting more detail#
Enable nothing — there is no debug mode to turn on. /vg status is the diagnostic
surface, and the server log carries the detail:
grep -i vonixguardian logs/latest.log
For the JSON-Lines audit trail:
tail -n 20 logs/vonixguardian/audit-$(date -u +%F).log.jsonl
If you are filing an issue, include the /vg status output, the startup log lines
around Booting VonixGuardian, and the exact command that failed. Redact database
credentials and world coordinates you do not want public.