Guidefor developers5 min read
plugin.yml api-version values for Minecraft 26.x servers
Bukkit api-version accepted 1.13 through 1.21, then calendar values arrived. What to declare for 26.1 and 26.2, and why a wrong value blocks loading.
What api-version actually does
api-version is a contract statement, not a dependency declaration. It tells the server which generation of the Bukkit API your jar was compiled against, so the server can decide whether to load the plugin directly, load it through a compatibility layer, or refuse it outright. The server reads it while parsing the descriptor, before any of your code runs.
Three consequences follow from that:
- The value names a version family, not a specific release.
- It is checked against a fixed list compiled into the server jar you are running.
- A wrong value is a load-time failure. Your plugin never gets an
onEnablecall, and anything that lists it as a dependency fails to enable as well.
Accepted values before the calendar change
The field arrived with Minecraft 1.13, the release that flattened block and item identifiers. Before that there was nothing to declare, because there had been no break large enough to need one. From 1.13 onward, each new minor family added one accepted value.
| Value | First release in family | Last release in family |
|---|---|---|
| 1.13 | 1.13, 18 July 2018 | 1.13.2 |
| 1.14 | 1.14, 23 April 2019 | 1.14.4 |
| 1.15 | 1.15, 9 December 2019 | 1.15.2 |
| 1.16 | 1.16, 23 June 2020 | 1.16.5 |
| 1.17 | 1.17, 8 June 2021 | 1.17.1 |
| 1.18 | 1.18, 30 November 2021 | 1.18.2 |
| 1.19 | 1.19, 7 June 2022 | 1.19.4 |
| 1.20 | 1.20, 2 June 2023 | 1.20.6 |
| 1.21 | 1.21, 13 June 2024 | 1.21.11, 9 December 2025 |
The 1.21 family is the useful one to look at. It ran for twelve releases across roughly eighteen months, and one api-version value covered all of them. That is the granularity the field works at.
Quote the value. This is the single most common way the field goes wrong:
api-version: 1.20 # YAML reads this as the number 1.2
api-version: "1.20" # correct
Unquoted, 1.20 is a valid floating point literal, so the YAML parser hands the server 1.2, which is not an accepted value. The same trap is waiting in the calendar era for any family whose second component ends in a zero.
Declaring 26.1 and 26.2
Calendar versioning changed the version numbers, not the rule. 26.1 and 26.2 are families in exactly the same sense that 1.21 was.
| Value | Covers | Java required |
|---|---|---|
| 26.1 | 26.1, 26.1.1, 26.1.2 | 25 |
| 26.2 | 26.2 | 25 |
So for a plugin targeting the current release:
name: ExamplePlugin
version: 1.0.0
main: com.example.ExamplePlugin
api-version: "26.2"
There is no value that means "1.22". That release never existed, and no server jar has it in its accepted list. If you see api-version: "1.22" in a plugin, it was written by someone guessing at the numbering, and it will fail on every server ever built.
What happens when the value is unknown to the server
The server compares your declared value against its own compiled list. If the value is absent from that list, the plugin is rejected during load with an unsupported API version message and never reaches onEnable.
That happens in three situations:
- You declared a newer version than the server runs. A jar declaring
26.2will not load on a 1.21.11 server. The 1.21.11 jar was built in December 2025 and has no knowledge of a value that did not exist until March 2026. - You declared something that is not a real family.
1.22,26.2.1,1.21.11and26all fall here. - YAML rewrote your value. The unquoted number case above.
The failure is loud and immediate, which is the point of the design. A plugin compiled against API surface the server does not have would otherwise die with a NoSuchMethodError somewhere deep in a listener, at whatever moment the missing method was first reached.
Legacy mode and why you do not want it
Omitting api-version entirely is not the same as declaring an old one. A descriptor with no api-version key is treated as pre-1.13 and the server applies a translation layer to it: material names, data values and enchantment identifiers get mapped from the old world into the current one on every call that crosses the boundary.
This exists so that plugins abandoned before 2018 keep working. It is not a target you should be building for. Legacy mode adds a per-call cost, it cannot translate anything added after 1.13, and it silently changes the meaning of some API calls rather than failing on them. It is also not guaranteed to survive. Whether a given calendar-era server still loads a descriptor with no api-version at all is a property of that specific server build, so test it rather than assuming it.
If your plugin is new, always declare a value.
Choosing the lowest value you can still support
api-version sets a floor, not a ceiling. Declaring 1.21 on a 26.2 server is fine as long as 1.21 is still in that server's accepted list, and it means your jar loads on 1.21 servers too. The cost is that you accept every behaviour change made to the API since that value, without the server warning you about any of them.
A workable rule:
- Declare the oldest family you actually build and test against.
- Raise the value when you start calling API that did not exist earlier, not on a schedule.
- Never declare a family newer than the oldest server you want to support, because that is a hard rejection rather than a degradation.
Where a single value cannot cover your range, ship two jars rather than one jar with a value that lies. Check the Paper build history for which game versions currently have builds, and the 26.2 compatibility page for what the rest of the ecosystem has declared support for.
What could change
The accepted value list grows with every new Minecraft family, so 26.3 becomes a valid value when that release ships and not before. Server projects also periodically drop the oldest entries and the legacy translation layer, which turns a previously working old value into a rejection. Both changes are observable in one place: the accepted list compiled into the server jar you are targeting. Read it from the jar itself, or check the release notes for the build you downloaded from the Paper page. Version families and their release dates on this site come from Mojang's own version manifest, listed at all Minecraft versions.