Annotation Interface RunAsync


@Target(METHOD) @Retention(RUNTIME) public @interface RunAsync
Marks a @CmdMapping method body as eligible to run off the main thread -- see below for when that is, and is not, actually safe.

An @RunAsync body must not touch world, entity, block or chunk state directly. Asynchrony here is reserved for pure-CPU or I/O work. The one Bukkit call an async body may make is scheduling its state-touching work back onto the main thread through BukkitScheduler.runTask(org.bukkit.plugin.Plugin, Runnable); this annotation never grants safe access to those APIs by itself. A handler whose body consists only of such state access has no reason to carry the annotation at all.

Without this annotation, a synchronous command body is not run inline on the calling thread -- the framework already defers it by one tick via runTask() in BaseCommandExecutor. Removing @RunAsync from a handler that never needed it is therefore not "making the command block the server"; it restores the same one-tick-deferred synchronous dispatch every other command already uses.

The observable failure when this rule is broken is not a slow command -- it is a crash. Bukkit's async-scheduler dispatch (see runTaskAsynchronously() in BaseCommandExecutor) runs the body off the main thread unconditionally, and the first call into world, entity, block or chunk state trips Paper's asynchronous-operation check and kills the command mid-handler.

For work that genuinely belongs off-thread -- and needs a processing message, a timeout, or configurable behavior around that boundary -- use AsyncCommand instead of this annotation.

See Also: