Frequently asked questions
Short answers to the questions every new plugin developer asks.
Short answers to the questions every new plugin developer asks, grouped by topic. Each answer ends with a link to the chapter that explains the topic properly, so you can read the quick answer first and dig deeper only when you need to.
Use the headings as a menu: press Ctrl+F and search for a word from your question, or jump to the topic that fits. If your plugin is misbehaving right now, start with the picture below, then read "When something does not work".
Getting started
Do I need to know Java before I start?
No, but you will learn it as you go. The guide has a Java track that teaches only the parts plugins really use (variables, decisions, loops, methods, classes), and every Java idea is tied to plugin code. You can begin with Your first plugin and read the Java chapters whenever a word confuses you. Start the Java track at How code reads.
What is a plugin, and how is it different from a mod?
A plugin is a jar file that adds features to a server by using the server's API. Players do not install anything: they join with normal Minecraft. A mod changes the game itself and usually must be installed by every player too. Read What is a plugin?.
What tools do I need?
Three: a JDK (Java Development Kit, version 25 for Paper 26.3), the IntelliJ IDEA editor, and Gradle, which installs itself through the project's wrapper. The page Your toolbox installs all of them step by step on Windows 11.
Which Java version do I need?
Java 25 or newer. Paper 26.3 will not start on older Java, and your build is set up to compile for Java 25. The recommended download is Eclipse Temurin 25. If a command says "release version 25 not supported", your tool is using an older Java, as described in The wrong Java version.
Can I write plugins in Kotlin or another language?
Yes, any language that runs on the Java Virtual Machine works, but Kotlin needs extra setup (its runtime library must be included). All the guide's examples and the Paper documentation use Java, so learn Java first.
Is there a faster way to find out what code does?
Yes. In IntelliJ, hover any word and press Ctrl+Q to read its documentation. Every code block in this guide also explains words when you hover them. See the IntelliJ survival guide and the Code Explainer.
Paper, Spigot and mods
What is the difference between Bukkit, Spigot and Paper?
Bukkit was the original plugin API. Spigot improved on it, and Paper is a fork of Spigot that is faster and adds a much bigger API (org.bukkit classes are still there, plus Paper's own). Most Bukkit and Spigot plugins run on Paper, but a plugin that uses Paper-only code does not run on Spigot. See What is a plugin?.
Why Paper and not Spigot?
Paper has better performance, many more events and helpers (such as sendRichMessage and the modern command system), and is the server most hosts and communities use today. This guide teaches Paper.
Can I put mods (Fabric, Forge, NeoForge) on a Paper server?
No. A Paper server runs plugins, not mods, and the two kinds are not interchangeable. Players can still use client-side mods such as shaders or minimaps on a Paper server, because those only change what their own screen shows. If you want server-side mods, you need a different server software.
Is Paper the same as vanilla Minecraft?
Paper plays like vanilla Minecraft by default, so players with unmodified Minecraft can join. It just gives plugins many more ways to change things. See What is a plugin?.
Why do tutorials online look different from this guide?
Many were written years ago. Old code uses ChatColor, "&a" codes and setDisplayName(String), which are deprecated ("replaced, do not use for new code"). Paper 26.3 uses Adventure text and MiniMessage instead. When an old example does not compile, look for its modern version in Text and colors with Adventure or the Paper cheat sheet.
Building and installing
Why Gradle and not Maven?
Both work. This guide uses Gradle because the test server tool (runServer) is built on it, and one command starts a ready server with your plugin. Maven is common too, and every build topic on the Gradle and Maven reference has both versions.
Where is my plugin jar after building?
With Gradle, in the build\libs folder of your project. With Maven, in target. The name is the project name plus the version, such as my-plugin-1.0.0.jar. See Build, install and test.
How do I install my plugin on a server?
Stop the server, copy the jar into its plugins folder (delete any older jar of the same plugin first), and start the server again. On a hosting panel, upload the jar into the plugins folder using the panel's file manager. Check /plugins: your plugin should be green. Details are in Installing on a real server.
Why is /reload bad?
It unloads and reloads every plugin without going through a real restart, which leaves old copies of classes in memory and breaks plugins that keep data between enables. Paper warns you about it itself. Always stop and start the server. The reason in depth is in Why never reload.
How do I add a library (dependency) to my plugin?
Add one line to your build file, and if the server does not already have the library, shade it into your jar so it travels with your plugin. Both steps are explained, for Gradle and Maven, in Shading: packing a library into your jar.
How do I use another plugin, like Vault or PlaceholderAPI?
Add its API as compileOnly, list it in depend or softdepend in plugin.yml, and check that it is installed before using it. Working with other plugins shows how, with real examples.
What is the difference between plugin.yml and paper-plugin.yml?
plugin.yml is the classic file every plugin can use, and the right choice for almost everything you will build. paper-plugin.yml is a newer Paper-only format with extra features such as a bootstrapper, and it handles commands and dependencies differently. See plugin.yml explained and Paper plugins.
Updating and versions
How do I update my plugin to a new Minecraft version?
Change paperVersion and minecraftVersion in gradle.properties (the script update-paper-version.ps1 in paper-templates does the first one for you), set api-version in plugin.yml, reload Gradle in IntelliJ, then build. Read the compiler's complaints: a method that was removed or renamed shows up as a red error, and deprecated ones as warnings. Fix those, run the test server and try each feature. See Build, install and test.
What does api-version do?
It tells Paper which Minecraft version your plugin was written for. If it is newer than the server, Paper refuses to load the plugin. If it is missing, Paper treats your plugin as ancient and runs a slow compatibility mode. Always write it, and quote it: '26.3'. See plugin.yml explained.
How do I make one plugin work on several Minecraft versions?
Compile against the oldest Paper version you want to support, set api-version to that version, and avoid anything newer. Do not use server internals (NMS), because those change with almost every release. Then test the jar on every version you claim to support. Checking whether a class or method exists at runtime is possible but advanced; most plugin authors simply support the current version. Internals are covered in Server internals.
Does my plugin work on Folia?
Only if you wrote it for Folia. Folia splits the world into regions that tick on different threads, so the normal scheduler does not work, and your plugin must say folia-supported: true in plugin.yml. Learn it in Folia and regionized scheduling.
When something does not work
My listener does not fire. What do I check?
Work down this list. First, did you register it in onEnable with registerEvents(listener, this)? Second, does the method have @EventHandler, exactly one parameter, and does the class say implements Listener? Third, is it the right event? Some events fire in ways you do not expect: PlayerInteractEvent runs once for each hand. Fourth, did another plugin cancel it first, or did you set ignoreCancelled? Add a getLogger().info("fired") line at the top of the method to see whether the method runs at all. See Events and listeners and Event priorities and canceling.
My command says "Unknown or incomplete command". Why?
That is the message Minecraft shows for a command nobody registered. If your plugin uses the classic way, the command must be listed under commands: in plugin.yml and you must set its executor in onEnable. If it uses Brigadier, it must be registered in the commands lifecycle event. It also appears when the plugin failed to load, so look at /plugins and the console first. Read Commands, part 1, part 2 and part 3.
My plugin does not show up in /plugins or shows in red.
Paper could not load it. Scroll up in the console to the first red line after the server starts: it says why. Usual reasons: plugin.yml is missing or in the wrong folder (it belongs in src/main/resources), main does not match the real package and class name, or the Java version is too old. Practice reading this in Build, install and test and look the message up in the Error encyclopedia.
Why do my colors show weird symbols like §?
Your text is being read with the wrong encoding. Set the compiler to UTF-8 (options.encoding in Gradle, project.build.sourceEncoding in Maven) and make sure IntelliJ saves files as UTF-8. Better still, avoid the old section symbol completely and use MiniMessage tags such as <green>. See MiniMessage.
Why does &a show up literally in chat?
The ampersand is not a color code in Minecraft itself, only a convention that old plugins translate. Paper does not translate it for you. Use MiniMessage in your own text. If you must read old-style codes from a config file, convert them with LegacyComponentSerializer.legacyAmpersand(), as this listener does:
Where this file livesref-faq-answerssrcmainjavacomexamplereffaqanswersJoinTracker.java
The package com.example.reffaqanswers is the folder path com/example/reffaqanswers inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- ref-faq-answers/
- src/main/
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- FaqAnswersPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinTracker.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- config.ymlDefault settings that server owners can change
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
26@EventHandler27public void onJoin(PlayerJoinEvent event) {28 Player player = event.getPlayer();29 joinedThisSession.add(player.getUniqueId());30 String template = plugin.getConfig().getString("welcome", "");31 String filled = template.replace("{name}", player.getName());32 Component message = LegacyComponentSerializer.legacyAmpersand().deserialize(filled);33 player.sendMessage(message);34}- Reads the text from
config.yml. The second argument is the value to use when the key is missing. - A very simple placeholder: swaps the text
{name}for the player's name. - Turns
&a-style codes into a real coloredComponent. This is a bridge for old configs, not the way to write new messages.
More in Text and colors with Adventure.
I got a NullPointerException. What does it mean?
Your code used something that was null, which means "nothing here". The most common source in plugins is looking up a player who is not online, which returns null. Read the first line of the error that mentions your own package: it shows the exact line. See Null, exceptions and stack traces and the Error Doctor.
My code freezes the whole server. Why?
The server runs almost everything on one main thread, a single line of work that handles each game tick in turn. A long task there, such as reading a big file, a database or a web page, stops the game for everyone. Move slow work to an async task (one that runs beside the game instead of inside it), then return to the main thread for any Bukkit calls. See Threads, performance and lag and the scheduler.
Why does the server say my code must run on the main thread?
Most Bukkit API calls (changing blocks, teleporting, opening inventories) are only allowed on the main thread. If you call them from an async task, you get an error. Schedule the call back onto the main thread, as the scheduler chapter explains.
IntelliJ shows red errors, but the build works. What is wrong?
IntelliJ has not reloaded the project after you changed a build file. Click the Gradle reload button that appears, then wait for the progress bar. If it still looks wrong, check the SDK under . See the IntelliJ survival guide.
Writing code
How do I get a player by name?
Use Bukkit.getPlayerExact(name). It returns the online player with exactly that name, or null if nobody is online with it, so always check. Do not use Bukkit.getPlayer(String) unless you want it to guess: it matches the closest name. To find someone by UUID, use Bukkit.getPlayer(uuid). The null check is shown in the next answer. See Working with players.
How do I give a player an item?
Add it to their inventory. addItem returns the items that did not fit, and a careful plugin drops those on the ground instead of letting them vanish:
Where this file livesref-faq-answerssrcmainjavacomexamplereffaqanswersJoinTracker.java
The package com.example.reffaqanswers is the folder path com/example/reffaqanswers inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- ref-faq-answers/
- src/main/
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- FaqAnswersPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinTracker.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- config.ymlDefault settings that server owners can change
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
40public boolean giveDiamond(String playerName) {41 Player target = Bukkit.getPlayerExact(playerName);42 if (target == null) {43 return false;44 }45 Map<Integer, ItemStack> leftovers = target.getInventory().addItem(ItemStack.of(Material.DIAMOND));46 for (ItemStack item : leftovers.values()) {47 target.getWorld().dropItem(target.getLocation(), item);48 }49 return true;50}- Finds the online player with exactly this name.
- Nobody is online with that name. Stop before touching
target, which would crash. - Puts the diamond into the first free slot. The returned map holds whatever did not fit, so it is empty when everything was added.
- Drops each leftover item at the player's feet.
More in Items and ItemStacks and Inventories.
How do I run code every X seconds?
Use the scheduler's runTaskTimer. Time is counted in ticks: 20 ticks are one second. The example below broadcasts a tip every minute:
Where this file livesref-faq-answerssrcmainjavacomexamplereffaqanswersFaqAnswersPlugin.java
The package com.example.reffaqanswers is the folder path com/example/reffaqanswers inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- ref-faq-answers/
- src/main/
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- FaqAnswersPlugin.javayou are hereMain class: Paper starts here (named as main in plugin.yml)
- JoinTracker.javaListener: reacts to events
- resources/Files copied into the jar as they are
- config.ymlDefault settings that server owners can change
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
17private void startTipBroadcast() {18 long everyMinute = 60L * TICKS_PER_SECOND;19 getServer().getScheduler().runTaskTimer(this, task -> {20 String tip = getConfig().getString("tip", "");21 getServer().broadcast(MiniMessage.miniMessage().deserialize(tip));22 }, everyMinute, everyMinute);23}- Named after what it means. Sixty seconds times 20 ticks is 1200 ticks.
- Starts a repeating task. The two numbers are the delay before the first run and the gap between runs, both in ticks.
- Sends a message to every player on the server.
Handy converter: the Tick Calculator. Learn it in the scheduler chapter.
How do I send a colored message?
The shortest way is player.sendRichMessage("<green>Hello"), which reads MiniMessage tags. Try tags out in the MiniMessage Studio and read MiniMessage: easy formatted text.
How do I send a message to everyone?
getServer().broadcast(component) sends a Component to every player, as in the timer example above. To reach only some players, loop over getServer().getOnlinePlayers() (see Loops).
How do I teleport a player?
Create a Location and call player.teleport(location). The teleportAsync version loads the destination chunk without freezing the server and is what Folia needs, so prefer it when the target may be far away. See Worlds, locations and movement.
How do I check a permission?
player.hasPermission("myplugin.use") returns true or false. Declare the permission in plugin.yml so Paper knows its default, and let server owners hand it out with a permissions plugin. See Permissions.
How do I make a cooldown or a toggle?
Store the time of the last use (or a flag) per player, keyed by the player's UUID, and compare against the current time next time. Patterns for both are in Patterns: cooldowns, toggles and player state.
How do I make a menu players can click?
A chest-style menu is an inventory with named items, plus a click listener that cancels the click and does something. Start with Inventories and then Building GUI menus.
Can I keep a Player in a field?
Do not. A Player object belongs to one login session; once that player leaves, your saved object is stale and holding it can leak memory. Keep the player's UUID (a long unique id that never changes) instead and look the player up when you need them. The listener above does this with joinedThisSession:
Where this file livesref-faq-answerssrcmainjavacomexamplereffaqanswersJoinTracker.java
The package com.example.reffaqanswers is the folder path com/example/reffaqanswers inside src/main/java: every dot in the package name is one folder. IntelliJ creates these folders for you when you make a new package.
- ref-faq-answers/
- src/main/
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- FaqAnswersPlugin.javaMain class: Paper starts here (named as main in plugin.yml)
- JoinTracker.javayou are hereListener: reacts to events
- resources/Files copied into the jar as they are
- config.ymlDefault settings that server owners can change
- plugin.ymlTells Paper the plugin's name, version and main class
- java/com/example/reffaqanswers/Package com.example.reffaqanswers
- build.gradle.ktsThe build recipe: Paper 26.3 API, Java 25, how the jar is made
- gradle.propertiesVersion numbers used by the build
- gradlew.batRuns Gradle on Windows without installing it
- settings.gradle.ktsThe project's name
- src/main/
Gray files come with the project template; you rarely edit them. Open the whole project in the Compile Lab, or download it from the project page.
20private final Set<UUID> joinedThisSession = new HashSet<>();- A
set remembers each value at most once. Storing UUIDs keeps the memory small and stays correct when players leave.
Read about UUIDs in Time, UUIDs and randomness.
Do I need NMS (server internals)?
Almost never. NMS is the server's own code, which changes with every Minecraft update and needs special build setup. Paper's API now covers nearly everything beginners want. Check the API first, and read Server internals only when you are sure it cannot do the job.
How do I find the right class, method or event?
Use autocomplete (Ctrl+Space), the Event Finder for events, the API map for "which class does what", and the official Paper documentation at docs.papermc.io. The real API is always the final truth: press Ctrl+click on a name in IntelliJ to read its source.
Saving data
How do I store data?
Choose by what the data is. Settings that server owners edit go in config.yml. Small facts about a player, item, block or entity go in the PersistentDataContainer, which the game saves for you. Large or searchable data (balances, logs, leaderboards) goes in your own file or a database. Start with Configuration files, PersistentDataContainer and Saving player data.
How do I create the default config.yml?
Put a config.yml in src/main/resources and call saveDefaultConfig() in onEnable. It copies the file into the plugin's data folder if one does not exist yet, and never overwrites the server owner's edits. Read values with getConfig(), as the timer example above does for its tip text. To pick up edits without restarting, call reloadConfig(). See Configuration files.
Why is my saved data gone after a restart?
Variables in your classes live only in memory and disappear when the server stops. Anything you want to keep must be written to a file, a database or the persistent data container, and read back in onEnable. See The main class and plugin lifecycle.
Players, testing and Bedrock
How do I test with two accounts, legally?
Many features need a second player: trades, teleport requests, chat. The honest ways are to ask a friend to join your test server, or to buy a second Minecraft account. Do not use cracked clients, and do not set online-mode to false on a server other people can reach, because then anyone can pretend to be anyone. Many things can be tested alone: use /gamemode, /op and the console. See Testing your plugin and the Test Server tool.
Can Bedrock Edition players use my plugin?
Not directly: Paper is a Java Edition server. A separate project called Geyser lets Bedrock players join a Java server, and Floodgate lets them join without a Java account. Bedrock players then appear as ordinary players to your plugin, but some things look or behave differently (menus, names with a prefix, certain visuals), so test with a real Bedrock device if you want to support them. Setting up Geyser is outside this guide.
How do I debug my plugin?
Start simple: add getLogger().info(...) lines to see which code runs and what values it has. Then learn IntelliJ's debugger, which can stop your plugin on any line and show every variable. The test server's Debug button attaches it for you. See Debugging like a pro.
Learning and sharing
Can I use an AI assistant to write my plugin?
You can, and it helps, but check its work. AI tools often write old code (ChatColor, deprecated methods, wrong event names) because older tutorials are everywhere. Ask for Paper 26.3 and the modern API, compile the result, and make sure you understand each line. See Using AI assistants well.
Can I share or sell my plugin?
Yes. Pick a license first, because without one other people cannot legally reuse your code, and then publish on a site such as Hangar or Modrinth. Selling is allowed too, but read the rules of the place you sell on. See Releasing and sharing your plugin.
What should I build next?
Build the projects: the welcome kit is a good first one. Each project combines several chapters into a working plugin. Where to go next has more ideas.
Practice
Find the fix
Three plugins are broken. For each, say which of the four checks in the picture at the top you would do first, and what you would look for.
- After installing the jar,
/pluginsdoes not list the plugin at all. - The plugin is green, but nothing happens when players break blocks. Your
BlockBreakEventmethod has@EventHandler. - The listener runs (you see your log line), but players still break the block you wanted to protect.
Hint
Match each symptom to one of the four numbered boxes: loaded, registered, stopped, logged.
Show the solution
- Check 1, did it load? Read the first red line in the console. Usual causes:
plugin.ymlmissing or in the wrong folder, or a wrongmain. - Check 2, is it registered? The method is fine, but
onEnableprobably never callsregisterEventsfor the listener. - Check 3, is something stopping it? Your handler ran, so the problem is its logic: you forgot
event.setCancelled(true), the permission check lets everyone through, or another plugin with a later priority undoes it.
Safer diamond giving
Read the giveDiamond code above. What happens when the player's inventory is completely full, and what happens when the name is typed wrongly? Then describe what you would change so that the caller learns which of the two cases happened.
Hint
Look at what giveDiamond returns in each case, and what leftovers holds.
Show the solution
With a full inventory, addItem returns the diamond in leftovers, and the loop drops it at the player's feet; the method still returns true. With a wrong name, getPlayerExact returns null, and the method returns false before doing anything. Both cases are handled safely, but true does not tell the caller that the item was dropped. To report it, return a small enum or a message, such as GIVEN, DROPPED and PLAYER_NOT_FOUND, instead of a plain boolean. Enums are explained in Enums, records and modern Java.
Recap
- Paper is a faster, bigger fork of Spigot. It runs plugins, not mods, and Java Edition players join it directly.
- Build with Gradle or Maven, install the jar in
plugins, and always restart instead of using/reload. - When nothing happens, check in order: did it load, is it registered, is something stopping it, and what do the log lines say.
- Find players with
getPlayerExactand always check fornull. Keep UUIDs, notPlayerobjects. - Use
runTaskTimerfor repeating work (20 ticks per second), and never block the main thread. - Use MiniMessage for colors,
config.ymlfor settings and the persistent data container or a file for data. - Test with a friend or a second account you own, never with cracked clients.
Quick quiz
Your listener class and method look perfect, but nothing happens when a player joins and there is no error. What is the most likely cause?
Paper only calls listeners that were registered, and an unregistered one produces no error./reloadis never the answer, andplugin.ymlhas no switch for events.Which is the safest way to remember which players you have already welcomed?
APlayerobject goes stale when the player leaves and can leak memory. A UUID never changes, even if the player changes their name.How many ticks make one second on a server that is running normally?
A tick is 50 milliseconds, so 20 of them fit in a second. 60 is the number of seconds in a minute, which is a common mix-up.A player types
/homeand sees "Unknown or incomplete command". What should you check first?That message means no plugin registered the command. Either the plugin failed to load or the registration code never ran. Memory and XP have nothing to do with it.Can you put a Fabric mod in a Paper server's
pluginsfolder?Mods and plugins use different APIs. Players can still use client-side mods on a Paper server, because those only change their own game.
Next steps
- Start of the guide if you want the full path from the beginning.
- The error encyclopedia for exact error messages.
- The Gradle and Maven reference for build questions.
- The glossary for any word you do not know.