// voxen field notes

How to Fix Minecraft Plugin Errors (9 Build & Runtime Fixes)

· updated · 8 min read · [ TUTORIAL ] · by Matteo Orazietti

To fix a minecraft plugin error, read the first Caused by line in the log: it names the real failure while everything above it is just the chain that carried it. Nine failures cause nearly every broken build and every dead-on-arrival .jar — a bad plugin.yml, a wrong API version, dependency and Java mismatches, crashes on enable, and silent command or permission faults. Below each one comes with its log signature, its cause, and the exact fix, in the order you will meet them.

How do you read a plugin error log without panicking?

Every stack trace below follows the same anatomy, and learning it once makes all nine fixes faster. Read bottom-up: the top frames describe where the server noticed the problem, the Caused by lines describe what actually broke, and the first Caused by is usually the only one that matters. Lines naming your plugin package (for example com.yourname.coolplugin) mark your code; lines naming org.bukkit or java.lang mark the platform reacting to it. Fix the cause, not the reaction. When the log is long, search for these three strings in order:

  1. Could not load / Could not enable — tells you the phase: load-time means the .jar or its descriptor is wrong; enable-time means your onEnable threw.
  2. Caused by — the first one names the exception to fix; later ones are consequences.
  3. Your package name — the first frame inside your own classes is the line to open in the editor.

Keep a staging server for this loop — never debug on production. The procedure in our installation guide for Spigot, Paper and Purpur doubles as a debugging setup: same version, same Java, full startup log visible.

Why does the server refuse to load the .jar?

Load-time failures happen before a single line of your code runs. The server inspected the .jar, disliked something structural, and said so. Two errors cover almost all of them.

1. InvalidDescriptionException — the plugin.yml problem

The signature is InvalidDescriptionException: Invalid plugin.yml followed by something likemain is not defined or a YAML complaint. Causes, in frequency order: the mainkey points at a class that does not exist or is misspelled; indentation is broken (tabs instead of spaces, or a list item at the wrong depth); a required key (name, version,main) is missing. The fix is mechanical: open plugin.yml, confirmmain matches the fully qualified class name exactly, re-indent with spaces, and validate the YAML in any linter before rebuilding (the plugin.yml guide lists every field). Commands referenced in code but missing from plugin.ymldo not cause this error — they cause the silent failure in section five — so do not conflate the two.

2. Unsupported API version — the api-version problem

The signature is Unsupported API version 1.x at load. Your api-version declares a newer API than the server provides — typically a .jar built for 1.21 dropped onto a 1.20 server, or a patch version like 1.21.4 declared on a server older than 1.20.5, which only understands values like 1.20. The fix: setapi-version to the server version you actually run, and rebuild against that version API. On 26.x the value is the year-based major.minor: write '26.1' for a 26.1 server, not '26' (a bare 26 failed to load in a reported case). A server refuses any plugin whose api-version is newer than itself, so a plugin declaring '26.2' will not load on 26.1. If you must support several versions from one .jar, target the oldest and avoid newer-only calls — or accept separate builds. This error overlaps heavily with version migration: if the plugin is also old, read our guide to updating to 1.21 and 26.x alongside this fix.

terminal — load-time failures
Could not load 'plugins/MyPlugin.jar': Invalid plugin.yml: main is not defined Could not load 'plugins/MyPlugin.jar': Unsupported API version 1.21 (server is 1.20.4)

Why does the build fail before producing a .jar?

Build failures are kinder than runtime ones: the compiler tells you the file and line. The two below account for most red builds on plugin projects, and both are environment mistakes rather than logic bugs.

3. Dependency resolution failure — wrong repository or artifact

Maven or Gradle cannot find the API artifact: Could not resolve dependencies orCould not find artifact. Usual causes: building Paper plugins against the Spigot repository only (Paper artifacts live in their own repository), a version string that does not exist, or compiling offline after clearing the cache. Fix by declaring the repository that matches your dependency with the version string copied from the official docs rather than memory. One project, one platform artifact: depending on both Spigot and Paper APIs in the same build is how phantom NoSuchMethodError crashes are born later.

4. Class file version mismatch — the Java release problem

The signature is class file has wrong version 65.0, should be 61.0 (numbers vary) orinvalid target release. You compiled with a newer Java than the server runs — or set a newer maven.compiler.release than the toolchain provides. Minecraft 1.20.5 to 1.21.x needs Java 21, and 26.1 and newer need Java 25; 1.17–1.20.4 need Java 17; 1.8–1.16 era servers need Java 8 or 11 depending on build. The 26.x case is a Java 25 build (class file version 69.0) landing on a Java 21 server, which only accepts up to 65.0. Fix the alignment in both directions: the release in your build file must be less than or equal to the JDK running the server, and ideally you compile with the same major JDK the server uses. Verify withjava -version on the host before touching anything else — half of these reports end there. The Java version guide has the full table.

terminal — build failures
[ERROR] Could not resolve dependencies: Could not find artifact org.spigotmc:spigot-api:jar:1.21-R0.1 [ERROR] class file has wrong version 65.0, should be 61.0 (server runs Java 17, build used Java 21)

Why does the plugin crash the moment it enables?

Enable-time crashes are the most dramatic and usually the simplest: the .jar is structurally fine, butonEnable — or something it calls — throws. Three culprits dominate.

5. NoClassDefFoundError — the missing or unshaded dependency

The plugin loads, then dies on first use of a library class: NoClassDefFoundError naming a package like a database driver or HTTP client. The library was on your compile classpath but never made it into the .jar — you forgot to shade it, or marked it provided when the server does not provide it. Fix: shade libraries the server does not ship into the final .jar with relocation to avoid clashes, and keep provided scope strictly for the server API itself.

Not a missing library: from 26.1 Mojang ships unobfuscated server jars, and Paper removed its internal plugin remapper. A plugin that reaches into net.minecraft with Spigot-mapped names such as EntityHuman does not work on 26.1+, on Paper or Spigot, and fails with NoSuchMethodError or NoClassDefFoundError naming a net.minecraft class. Plugins that use only the Bukkit/Spigot/Paper API are not affected. The fix is a new plugin build against the 26.x server, not a dependency change.

6. NullPointerException from getConfig — the missing default

Cannot invoke ... because getConfig().getString(...) is null inside onEnable is almost always a config key your code assumes and the file does not contain: a fresh install without defaults copied, a renamed key after an update, or saveDefaultConfig() never called. Fix in two places: call saveDefaultConfig() so first installs get sane defaults, and code defensively — supply fallback values for every lookup and validate required sections at startup with a clear error naming the missing key. A plugin that disables itself withMissing required config key: rewards is infinitely kinder than one that NPEs ten lines later.

7. Plugin already initialized — the double-register slip

IllegalArgumentException: Plugin already initialized means something constructed your main class twice: a new MyPlugin() somewhere (managers holding their own instance instead of the server-owned one), or a hot-reload tool fighting a static singleton. Fix: never instantiate the main class yourself — pass the server-created instance into constructors — and restart properly for anything conclusive instead of trusting hot-reload.

terminal — enable-time crashes
Caused by: java.lang.NoClassDefFoundError: com/zaxxer/hikari/HikariConfig Caused by: java.lang.NullPointerException: Cannot invoke "String.length()" because getConfig().getString("prefix") is null Caused by: java.lang.IllegalArgumentException: Plugin already initialized!

Why do commands and permissions silently do nothing?

The cruelest category: no error anywhere, and nothing happens. Both causes are registration mistakes, which is why the log stays quiet — as far as the server knows, nothing was supposed to happen.

8. Unknown command — registered in code but not in plugin.yml

Typing the command prints the usage or unknown-command message, and the executor never fires. The executor is registered in code but the command is missing from plugin.yml, or the names differ by a letter or a case. getCommand("crate") returns null when plugin.ymldefines crates, and calling setExecutor on that null is itself an NPE some servers swallow at enable. Fix: define every command in plugin.yml with identical spelling, then guard the lookup — if getCommand() returns null, log which name is missing and disable with that message instead of crashing.

9. Permission defaults that lock everyone out (or in)

The command runs for ops but nobody else, or a supposedly restricted admin command works for everyone. The permission node checked in code does not match the node declared in plugin.yml, or thedefault flag is wrong (op vs true vs false). Code checking shop.admin while the file declares shops.admin fails closed for every non-op, silently. Fix: keep one canonical list of nodes, declare each with an explicit default, and test every command twice — once as op, once as a plain player with only the intended nodes.

What is the fastest fix loop when nothing works?

When several of the above stack up — typically after a version jump — stop fixing symptoms one at a time and run the loop: reproduce on staging, read the first Caused by, fix exactly that, rebuild clean, retest. One error per cycle; the second error in the log is usually the first error wearing a costume. If the chain runs through decompiled code from a .jar whose source is gone, the loop still works but each cycle is slower — that is the point where generating the fixed plugin with Voxen beats archaeology: describe the intended behavior plus the error, and it produces a clean project against your platform and version instead of a patched decompilation. Supported platforms and versions are listed in the Voxen documentation.

Errors the compiler finds for you: Voxen builds compile in the cloud against the right Spigot, Paper or Purpur API for your version — and when a build fails, the AI reads the compiler output and fixes the code itself instead of handing you a log. Free plan includes 5 generations with no card, Starter (€7.99/month) and Pro (€14.99/month) add capacity, BYOK (€3.99/month) uses your own key. Start at voxen.it and paste the error that brought you here.