Running a server? This topic in the server admin guide
Troubleshooting
Messages you may meet, what they mean and what to do. Build-time messages point at a file and line; runtime messages come from VersatiaCore while a plugin enables, reloads or disables and affect only that plugin. Every check the build makes, with its message, is listed in Compile-time checks.
At build time Plugin developer
No class extending org.bukkit.plugin.java.JavaPlugin found in the sources
The processor could not find your main class (or found several:
Several classes extend org.bukkit.plugin.java.JavaPlugin: ...). Make sure exactly one concrete
class extends VersatiaPlugin, or point the processor at plugin.yml:
ksp {
arg("versatia.pluginYml", layout.projectDirectory.file("src/main/resources/plugin.yml").asFile.path)
}
Dependency cycle: A -> B -> A
Two or more beans depend on each other through their constructors. Break the cycle, for example by letting one of them receive the other through a function parameter later, or by extracting the shared part into a third bean.
Dependency T of X is ambiguous; candidates: ...
Two beans of your plugin provide the requested type. Add @Qualifier("name") to the parameter or
@Primary to one of the classes. See
Names, qualifiers and primary beans. The variant
... is ambiguous while "Vault" is enabled means the candidates exist together only with that
plugin: mark one of them @Primary; see Conditional beans.
X depends on Y, which exists only with @ConditionalOnPlugin("Vault"); ...
Y is the only provider of what X needs, and it does not exist without Vault, while X does.
Make X conditional on the same plugins, declare a provider without the condition, or give the
parameter a default value to make it optional.
Bean name "x" is used by A, B
Two beans share a name (by default the decapitalised simple class name). Rename one with
@Qualifier("...") on the class.
X must have a primary constructor or exactly one constructor to be a bean
The runtime injects the primary constructor. Declare one, or keep a single constructor. The
related Injected parameter p of X must not be nullable (or must not be generic, must not be vararg) rejects a parameter the runtime cannot inject: use a non-null class type, or a default
value for a dependency that may be absent. See
What a bean can be.
X has @Qualifier more than once: written directly and inherited from @Fast; keep one
An attribute reaches the element twice, here once written and once through an
annotation of your own. Remove one
of them. Only @ConditionalOnPlugin may be repeated.
X cannot be enhanced: Y is neither the plugin's main class, an object nor a bean
@AutoInvoke or @Value sits on a member of a plain class. Move it to the main class, an
object or a bean, or make the class a bean. For @Value the message goes on ..., a bean nor a class a subclass enhancement creates, such as a Listener or a BackgroundTask: a plain Listener
or BackgroundTask may hold one. An @AutoInvoke function on such a class reads Y is created by a subclass enhancement, which sets only its @Value properties; give the class a bean scope such
as @LocalBean.
X.f must not be suspend to be enhanced; implement BackgroundTask ...
The full message is X.f must not be suspend to be enhanced; implement BackgroundTask for work that runs with the plugin, or launch from an injected PluginScope. The runtime calls an @AutoInvoke
function inside the event and does not wait for a coroutine. Work that runs as long as the plugin
is a BackgroundTask; anything else takes a PluginScope and
launches from it, in a plain function:
@AutoInvoke(Phase.PLUGIN_START_ONLY)
fun warmUp(scope: PluginScope) = scope.launch { cache.fill() }
@FriendlyName on X has no effect: it belongs on the plugin's main class
@FriendlyName names the plugin, so it goes on the main class, as one word. See
Friendly names.
@ConditionalOnPlugin on X has no effect: ...
The annotation sits on a class that is neither enhanced nor declares an enhanced function or
property, or on a function without an enhancement. Put it on a bean, a listener, an object
with @AutoInvoke functions or @Value properties, or such a function. On the main class the
message is @ConditionalOnPlugin cannot apply to X, the plugin's main class: the plugin itself
cannot be optional. See Optional plugins.
X.y must be a var to be enhanced
@Value sits on a val. The runtime sets the property, so it must be a var, or a val
holding a PreviousValueTracking. In a constructor the
message ends with declare the constructor parameter as a property: write var (or a val of a
PreviousValueTracking) before the parameter; see
In a constructor. The same family of messages rejects a
top-level property (move it into the plugin's main class, an object or a bean), a property of a
plain class and a function parameter (only properties can, constructor properties included).
@Value on X.y: T is not a supported type
The property's type is not one the runtime can read: the message lists what is
(scalars, enums, the Bukkit types, List, Set and Map<String, T> of scalars and enums, any of
them nullable). Name a mapper in @Value(mapper = ...) for a
structured value, or change the type; MutableList is not List.
@Value on X.y: mapper M maps to T, which U cannot hold
The ValueMapper<T> named in the annotation produces a type the property cannot hold; change
the property's type or the mapper. The related mapper M must be an object or a class with a no-argument constructor means the runtime could not create the mapper.
plugin.yml must list VersatiaCore under depend:
The checkPluginYml task of the example and template builds found a plugin.yml that would not
work: it does not parse, lacks name, version or main, or does not depend on VersatiaCore.
Fix the file; see plugin.yml.
Versatia plugins use plugin.yml; remove paper-plugin.yml
The same task found src/main/resources/paper-plugin.yml. Paper would load the JAR as a Paper
plugin, with an isolated class loader, instead of a Bukkit plugin depending on VersatiaCore.
Delete the file and keep the plugin's settings in plugin.yml; see
plugin.yml, not paper-plugin.yml.
Unresolved reference to a Versatia annotation
The build does not see VersatiaAPI: the VersatiaStarter (or VersatiaAPI) dependency is
missing, or the artifact is not in your local Maven repository. Run
./gradlew publishToMavenLocal in the VersatiaAPI and VersatiaStarter checkouts and check the
installation. A missing ksp plugin or VersatiaAPI-ksp
dependency does not show here: the build succeeds and the plugin fails at start with
Descriptor X not found.
At start-up
VersatiaCore is not running. Add it to depend: in plugin.yml.
Your plugin enabled before VersatiaCore or without it. Add VersatiaCore under depend: and make
sure the JAR is in plugins/.
Descriptor X not found
The full message is Plugin X: descriptor com.example.VersatiaDescriptor not found. Is the VersatiaAPI KSP processor applied to the plugin build?. The plugin JAR has no descriptor, so the code generator did not run. Apply the
com.google.devtools.ksp plugin and add ksp("dev.versatia:VersatiaAPI-ksp:...");
see In your build. The related
Plugin X: com.example.VersatiaDescriptor is not a Kotlin object; it is not a Versatia descriptor
means a class of your own uses the reserved name next to the main class: rename it.
Plugin X: descriptor D comes from ..., not from the plugin itself
The descriptor class next to your main class was found in another plugin or library your plugin
can see, not in your JAR: your build did not generate one, and another plugin has its main class
in the same package. Apply the KSP processor (above) and give your main class a package of its
own, such as com.example.myplugin.
Skipped X: Y is not installed
Not an error: X is marked @ConditionalOnPlugin("Y") and Y was not
enabled when your plugin started, so X does not exist until the next start; an X written
Class.function() is a function that is not called, while its class exists. The variant Y is installed but was not enabled before X; list it under softdepend: in plugin.yml means the server
started Y after your plugin: add Y under softdepend:.
LinkageError at start-up
java.lang.LinkageError: loader constraint violation: ... kotlin/jvm/functions/Function1 ...
Your plugin has a second copy of Kotlin in its own class loader. Either it lists kotlin-stdlib
(or kotlin-reflect) under libraries: in plugin.yml, or it lists a library built on Kotlin,
such as kotlinx-serialization-json, which brings kotlin-stdlib along. Remove the entry and shade
such a library without Kotlin, as shown in plugin.yml:
VersatiaCore is the single provider of the Kotlin runtime and your plugin reaches it through
depend:. The same happens when a plugin shades Kotlin or VersatiaAPI into its JAR.
kotlinx.coroutines is a special case: VersatiaCore provides it too, and your plugin must neither
list it nor shade it, not even without Kotlin. A second copy breaks with a LinkageError naming a
kotlinx/coroutines class, or a ClassCastException between two kotlinx.coroutines classes of
the same name, as soon as a coroutine reaches the framework: a PluginScope, a Condition,
delayTicks. A coroutine library for Bukkit such as MCCoroutine brings its own copy and the same
failure. Remove the entry or the dependency; the classes come from VersatiaAPI at compile time and
from VersatiaCore on the server.
Plugin X: no bean provides T, required by Y
No bean of your plugin and no exposed bean of a plugin enabled earlier provides T.
- If
Tbelongs to another plugin, that plugin must declare the bean@ExposeSingletonor@ExposePrototype(a@LocalBeanis invisible outside its plugin) and be listed under yourdepend:. - Check for a typo in the class name and that the providing plugin is installed.
- If your plugin can work without it, give the constructor parameter a default value; see Optional dependencies.
Plugin X: no bean provides dev.versatia.task.PluginScope, ...
VersatiaCore exposes PluginScope to every plugin, so this means the installed VersatiaCore is
older than the VersatiaAPI the plugin was built with: install the VersatiaCore matching that API.
Usually the descriptor format message
comes first. The related Plugin X: the PluginScope bean is C, not VersatiaCore's; background tasks and the stop order need VersatiaCore's means a bean of your plugin provides PluginScope
and shadows VersatiaCore's; remove it and inject the scope.
Plugin X: T required by Y is ambiguous; candidates: ...
Several plugins expose beans providing T. The candidates are listed with their owning plugin.
Add @Qualifier("name") to the parameter, or ask the providers to mark one @Primary.
ClassCastException when a bean from another plugin is injected
Your plugin bundles its own copy of the other plugin's classes. Declare that plugin as
compileOnly, never implementation, so that the class is loaded from the providing plugin
through depend:; nothing the server provides belongs in your JAR, see
In your build.
Plugin X: "n" (C from Y) cannot be injected as T: the two plugins load separate copies of that class
The runtime caught the case above before the cast: your plugin X and the provider Y each load
their own T. Either one of them bundles the classes of the other (declare that dependency
compileOnly), or Y was reloaded on its own by a plugin manager, which gives it new classes:
restart the server instead.
Plugin X: bean "n" (C) could not be created: ...
The constructor or an initialiser of bean C threw; the rest of the message and the cause are
what it threw. With for Z before the colon, the instance was being made for plugin Z, which
injects the @ExposePrototype bean C of X; fix C, or what Z passes it.
X was built for descriptor format A, but this VersatiaCore supports B
The plugin's descriptor format and the server's VersatiaCore do not match. Rebuild the plugin against the VersatiaAPI that matches the installed VersatiaCore, or update VersatiaCore. See Descriptor format compatibility.
Plugin X needs the enhancement processor Y, which is not available
The descriptor names a processor class that cannot be loaded through the plugin's class loader.
Either the plugin was built against a VersatiaAPI with enhancements this VersatiaCore lacks (update
VersatiaCore), or the processor belongs to another plugin that is not installed or not listed under
depend: (install it, add it to depend:), or the name in @VersatiaEnhancement is mistyped.
Plugin X: Y cannot be loaded to ... it
The descriptor names a class the JAR does not contain, usually because the JAR comes from an
incomplete or stale build. Run ./gradlew clean build and deploy the new JAR. The related
Y is not a Kotlin object, so there is no instance to ... means a class recorded as an object
was changed into a class without a rebuild.
Plugin X: Y.f() failed at START
Your auto-invoked function threw. The original exception is the cause; the message names the
function and the lifecycle event. At RELOAD the same message reaches the sender of
/versatia reload and the console, and the other plugins are still reloaded.
Plugin X: Y failed to reload
A bean implementing Reloadable threw from reload(); the original exception is the cause. The
runtime calls reload() once the plugin's beans exist and its values are set, and at every
reload of the plugin, so at start-up the plugin does not enable, and at a reload the message reaches the sender of
/versatia reload. The bean may belong to a module (an instance made for your plugin), in which
case the message still names your plugin, whose data folder it reads. See
Reloadable beans.
Plugin X: Y failed to close
A bean implementing AutoCloseable threw from close() while the plugin disabled. The other
beans are closed all the same; the original exception is the cause. See
Lifetime.
Plugin X: Y takes its consuming plugin as T, which Z is not
A prototype exposed by another plugin has a plugin-typed parameter, and your plugin is not an
instance of that type (for example, the prototype asks for VersatiaPlugin and your main class
extends JavaPlugin directly). Extend VersatiaPlugin, or ask the prototype's author for a wider
type. See Scopes.
Plugin X: Y.z could not be read from f.yml: ...
A @Value property could not be set: the rest of the message says why, with the full key path.
No value present at "k" means the key is missing and the property is not nullable; Value at "k" is not an int and friends mean the wrong shape, including a single value where a list
belongs. Nothing was set, so at start the plugin stays disabled and at a reload it keeps its
previous values. See Configuration values.
Plugin X: f.yml could not be read
The file is not valid YAML. The cause is the parser's message with the line and column; a tab used for indentation and a missing space after a colon are the usual culprits.
Plugin X: f.yml is missing from the data folder; ...
A @Value names a file the data folder does not have. When the plugin JAR ships a default copy,
the message says put it back, or reload the plugin to restore the default copy from the plugin JAR: run /versatia reload <name> and the file is extracted again. When it
does not, the message says the plugin JAR has no default copy: create the file, or, as the
plugin's developer, ship it under src/main/resources/resources/. Check the file argument of
the annotation too; the path is relative to the data folder.
Plugin X: the @Value file "f" lies outside the data folder; ...
A @Value names a file that, once its path is normalised, is not inside the plugin's data folder,
so the runtime refuses to read it. The compile-time step already rejects absolute paths and ..
segments written in the source; name a path relative to the data folder, such as config.yml or
rules/limits.yml.
Plugin X has no message "k"; ...
messages[key] of Messages found no such key; the message lists
the keys that start the same way, or says the plugin has no messages at all, which usually means
the messages/ directory is missing from its data folder: ship the files under
src/main/resources/resources/messages/. A key is dotted, shop.bought, and never contains the
file name.
Plugin X: messages/f.yml is not valid YAML: ...
A message file could not be read, so the plugin does not enable, or at a reload keeps its previous
messages. Its siblings: is not UTF-8 encoded; save it as UTF-8,
message "k" is defined twice in messages/f.yml, message "k" is defined in both messages/a.yml and messages/b.yml, "k" is a message in ... and a group of messages in ...; rename one of them,
message "k" in messages/f.yml is not text and ... is a list whose lines must all be text. Fix
the file and reload. See The files.
Plugin X: message "k" in messages/f.yml cannot be shown: ...
The message was read, but using it failed; the rest says why, such as
No argument "nmae"; the arguments are: name, item. Fix the brace in the file, or pass the
argument in the code. See Errors.
Plugin X: the friendly name "n" is already used by Y
Two running plugins declare the same @FriendlyName, or one declares as its friendly name the
plugin.yml name of another. Rename one of them; the second plugin stays disabled until then.
Plugin X: Y expects Z to supply parameter n, which it has not
For authors of enhancement processors: a constructor parameter is
marked as supplied by processor Z (as @Value does for a constructor property), but Z did not
call supply before the beans were created. The related the argument of parameter n of Y was supplied after the beans had been created means it called too late: supply in process, not in
onPluginEnhanced.
While the plugin runs
A task logs a failure
A background task, a coroutine of a PluginScope, a repeated run or a condition check threw; the
runtime logged it on the plugin's logger and the server goes on. The lines, what each means and
what follows are listed under Failures. The usual causes:
Background task X failed; it starts again at the next reload:run()threw; fix it and reload the plugin to start the task again.Background task X ended with TimeoutCancellationException although nobody cancelled it: awithTimeoutinsiderun()ran out; usewithTimeoutOrNull, or catch the exception.... a repeated run failed; it runs again on schedule ...: one run ofeveryTicksoreverythrew; the loop goes on and logs the same failure once.IllegalStateException: Asynchronous ...from Paper, logged asA coroutine failed: a Bukkit call insidewithContext(Dispatchers.IO) { }; move it after the block, back on the server thread.
Something runs twice after a reload
A coroutine launched from a PluginScope in a PLUGIN_RELOAD_OR_START function: the scope
survives the reload, so every reload starts one more copy. Make the work a
BackgroundTask, which the reload cancels and starts again, or
launch it from a PLUGIN_START_ONLY function.
Module with the Main dispatcher is missing
The code uses Dispatchers.Main, which exists on Android and in desktop UIs, never on a server.
Launch from the injected PluginScope, whose coroutines run on the server thread, or switch with
withContext(scope.serverThread) { }; see Threads.
No server clock in this coroutine: ...
delayTicks, everyTicks, every or condition was called in a coroutine that is not the
plugin's: one started with runBlocking, GlobalScope or a scope of your own. Launch it from the
injected PluginScope, or run it in a BackgroundTask.
A condition is created on the server thread, where its check runs; ...
scope.condition or condition was called on another thread, inside withContext(Dispatchers.IO)
or from an asynchronous event say. Create the condition in a bean, a start function or a coroutine
of the scope on the server thread; its triggers may still fire on any thread.
At a reload
Plugin X: reload must run on the server's main thread; ...
The reload was asked for from an asynchronous task or another thread; nothing was reloaded. Hand
it over to the main thread with plugin.runTask { plugin.versatiaReload() }; see
On the main thread. start must run ... and stop must run ... mean the same for a hand-made enhance or disable call.
dispatchConsoleCommand("...") must run on the server's main thread; ...
VersatiaEngine.dispatchConsoleCommand was called off the main thread, which Bukkit does not
allow; the command did not run. Wrap the call in plugin.runTask { ... }. The same wording, with
the name you pass, comes from VersatiaEngine.checkPrimaryThread("...") in your own code.
/versatia is an unknown command
VersatiaCore declares the command only when the CommandAPI plugin,
release 12.x, is installed. Drop its JAR into plugins/ and restart; see
Reloading plugins. Without it a plugin reloads itself with
versatiaReload(). If the log shows /versatia could not be registered. The installed CommandAPI ... may not be compatible ..., CommandAPI is there but in a release VersatiaCore cannot use;
install a 12.x release.
X failed to reload: ...
The reply of /versatia reload names the plugin and the chain of causes. A configuration mistake
is logged as one warning; anything else as an error with the stack trace. The entries above
explain the causes; the plugin keeps running with its previous state.
Y failed to reload after X, whose beans it uses: ...
X reloaded, and Y, which holds a bean X exposes with @Value properties (or wrapping one), failed in the reload that
follows it. X stays reloaded and Y
keeps its previous state; the rest of the message is the cause, as for any failed reload. Fix it
and reload Y.
Plugin X has already been reloaded by the reload under way; ...
A reload-phase function asked for a reload of a plugin that the running reload, its cascade included, already went through. Every plugin reloads at most once per reload, and a plugin using another one's beans already follows it; drop the call.
Plugin X is not running, ... is still starting, ... is already reloading
The plugin cannot be reloaded right now: it is disabled or its start failed, its start has not completed, or a reload is already in progress and a reload-phase function asked for another one. See Reloading plugins.
Debug mode
When a plugin does not behave as its code suggests, set debug: true in
plugins/VersatiaCore/config.yml and run /versatia reload VersatiaCore, then reload or restart
the plugin. VersatiaCore logs every step of each plugin's start, reload and stop at INFO:
[debug] Shop START: descriptor format 1.0, 11 elements, 9 for this event
[debug] Shop START: skipped VaultBridge: @ConditionalOnPlugin(Vault) is not met
[debug] Shop START: AutoInvokeEnhancementProcessor receives Counter.report()
[debug] Shop START: created 2 beans: "plugin" (com.example.shop.ShopPlugin), "counter" (com.example.shop.Counter)
[debug] Economy RELOAD: set 3 values, config.yml: base, rate, tax
[debug] Economy RELOAD: goes on to Shop, holding beans that follow its reloads
Values are logged by key only, never with what the file holds. Turn it off again the same way;
while it is off nothing is written. /versatia info <plugin> shows the same state at a glance;
see /versatia info.
Looking at what was generated
Whenever the behaviour surprises you, open
build/generated/ksp/main/kotlin/<package of the main class>/VersatiaDescriptor.kt. It lists every
element the runtime will act on: processor id, phase, provided types, dependencies with qualifiers,
and whether the call is direct or reflective. What is not in there does not happen.