Repository navigation
Commit e70bdb8
authored
Fix IndexOutOfBoundsException in AlternativeCommandsHandler.getAlternative (#6609)
<!--
EssentialsX bug fix submission guide
====================================
NOTE: Failure to fill out this template properly may result in your PR
being
delayed or ignored without warning.
NOTE: Don't type between any arrows in the template, as this text will
be
hidden. This includes this header block and any other explanation text
blocks.
Want to discuss your PR before submitting it? Join the EssentialsX
Development
server: https://discord.gg/CUN7qVb
EssentialsX is GPL
------------------
By contributing to EssentialsX, you agree to license your code under the
GNU General Public License version 3, which can be found at the link
below:
https://github.com/EssentialsX/Essentials/blob/2.x/LICENSE
Instructions
------------
If you are submitting a bug fix, please follow the following steps:
1. Fill out the template in full.
This includes providing screenshots and a link to the original bug
report. If there isn't an existing bug report, we recommend opening a
new
detailed bug report BEFORE opening your PR to fix it, else your PR may
be
delayed or rejected without warning.
You can open a new bug report by following this link:
https://github.com/EssentialsX/Essentials/issues/new/choose
2. When linking logs or config files, do not attach them to the post!
Copy and paste any logs into https://gist.github.com/, then paste a
link to them in the relevant parts of the template. Do not use Hastebin
or Pastebin, as this can cause issues with future reviews.
DO NOT drag logs directly into this text box, as we cannot read these!
3. If you are fixing a performance issue, please include a link to a
Timings and/or profiler report, both before and after your PR.
4. If you are fixing a visual bug, such as in commands, please include
screenshots so that we can more easily review the proposed fix.
(You can drag screenshots into the bottom of the editor.)
-->
### Information
<!--
Replace #nnnn with the number of the original issue. If this PR fixes
multiple issues, you should repeat the phrase "fixes #nnnn" for each
issue.
-->
This PR fixes #6608.
### Details
**Proposed fix:**
<!-- Type a description of your proposed fix below this line. -->
`getAlternative` prunes collected `WeakReference`s through its own
iterator, which can remove
every entry in a bucket. The following `commands.get(0)` then throws
`IndexOutOfBoundsException`.
This stores the first live alias during the iteration and returns that
instead, or `null` when
nothing live remains, which is what the caller already tests for
(`getAlternative(label) == null` in
`EssentialsPlayerListener.CommandSendFilter`).
Confirmed with an instrumented build that logged the list sizes around
the prune loop:
```
[6608] label=w thread=Paper Async Command Builder Thread Pool - 0
entry=2 removedByUs=2 after=0 expected=0
```
Both references in the `w` bucket had been collected and the loop
removed both.
Note: this does not address the possible concurrent modification also
raised in #6608
(`altCommands` is a plain `HashMap` of `ArrayList`, modified from the
async command-send path).
That needs a separate synchronisation decision.
**Environments tested:**
<!-- Type the OS you have used below. -->
OS: Debian 12
<!-- Type the JDK version (from java -version) you have used below. -->
Java version: 25.0.4 2026-07-21 LTS
<!--
Put an "x" inside the boxes for the server software you have tested this
bug fix on. If this feature does not apply to a server, strike through
the server software using ~~strikethrough~~. If you have tested on other
environments, add a new line with relevant details.
-->
- [x] Most recent Paper version (26.2, git-Paper-112)
- [ ] CraftBukkit/Spigot/Paper 1.12.2
- [ ] CraftBukkit 1.8.8
**Demonstration:**
<!--
Below this block, include screenshots/log snippets from before and after
as
necessary. If you have created or used a test case plugin, please link
to a
download of the plugin, source code and exact version used where
possible.
-->
Before, on every boot with a player join (5 consecutive boots,
EssentialsX 2.22.0 and
2.22.1-dev+17-382de4f, same server and plugin set):
```
[ERROR]: Could not pass event AsyncPlayerSendCommandsEvent to Essentials
java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
at java.util.ArrayList.get(ArrayList.java:428)
at com.earth2me.essentials.AlternativeCommandsHandler.getAlternative(AlternativeCommandsHandler.java:133)
at com.earth2me.essentials.EssentialsPlayerListener$CommandSendFilter.isEssentialsCommand(EssentialsPlayerListener.java:1279)
at net.ess3.provider.providers.PaperCommandSendListenerProvider.onAsyncCommandSend(PaperCommandSendListenerProvider.java:28)
...
```
Cause isolated with a temporary instrumented build logging list sizes
around the prune loop:
```
[6608] label=w thread=Paper Async Command Builder Thread Pool - 0
entry=2 removedByUs=2 after=0 expected=0
```
Both `WeakReference` in the `w` bucket had been collected and the loop
removed both.
After, with this fix (2.22.1-dev+18), same server, player join:
```
IndexOutOfBoundsException: 0
AlternativeCommandsHandler references: 0
Could not pass event AsyncPlayerSendCommandsEvent: 0
```1 parent 2dbd63a commit e70bdb8
1 file changed
Lines changed: 8 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
116 | 116 | | |
117 | 117 | | |
118 | 118 | | |
| 119 | + | |
| 120 | + | |
119 | 121 | | |
120 | 122 | | |
121 | 123 | | |
| |||
127 | 129 | | |
128 | 130 | | |
129 | 131 | | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
130 | 136 | | |
131 | 137 | | |
132 | | - | |
133 | | - | |
| 138 | + | |
| 139 | + | |
134 | 140 | | |
135 | 141 | | |
136 | 142 | | |
| |||
0 commit comments