GH-221 NPE in CollectionGui from null parcel content items#223
GH-221 NPE in CollectionGui from null parcel content items#223Jakubk15 wants to merge 6 commits into
Conversation
Empty/air inventory slots could slip past the GUI write filters and round-trip through the persister as null elements, causing a NullPointerException when CollectionGui read itemStack.getType() on a collected parcel. Normalize the item list in the ParcelContent record's compact constructor so null and empty stacks are dropped and the exposed list is immutable. This is the single chokepoint every read and write path goes through, so it heals already-corrupted rows on read and prevents bad writes. Adds Mockito to mock ItemStack in unit tests, since paper-api ItemStacks cannot be constructed without a running server (RegistryAccess). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4YqhTV42FvN95HanC7XJc
Legacy (Spigot map) ItemStack serialization drops empty/air stacks to null, the upstream cause of the issue #221 NPE. The jackson-bukkit Paper module defaults to an NBT byte-array format (ItemStack#serializeAsBytes) that round-trips empties safely, and its deserializer auto-detects the format, so previously stored legacy-format rows still read correctly and are rewritten in the new format on next save. The ParcelContent null/empty guard remains as defense in depth. Note: the serialization round-trip cannot be exercised by the existing test harness (paper-api ItemStacks require a running server, and the integration tests are Docker-gated), so this relies on the library's documented backward-compatible auto-detection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4YqhTV42FvN95HanC7XJc
There was a problem hiding this comment.
Code Review
This pull request addresses issue #221 by adding a guard in the ParcelContent constructor to filter out null and empty item stacks, and by updating ItemStackPersister to use default Paper serialization instead of legacy serialization. Additionally, Mockito was added to the test dependencies and new unit tests were introduced. The reviewer's feedback suggests optimizing the ParcelContent constructor to avoid stream overhead when the list is empty, and adding a unit test to verify that a null items list is handled correctly.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| ParcelContent content = new ParcelContent(UUID.randomUUID(), List.of(stone, air)); | ||
|
|
||
| assertEquals(List.of(stone), content.items()); | ||
| } |
There was a problem hiding this comment.
It would be beneficial to add a unit test to verify that the compact constructor correctly handles a null items list by defaulting to an empty list, ensuring full coverage of the defensive guard.
ParcelContent content = new ParcelContent(UUID.randomUUID(), List.of(stone, air));
assertEquals(List.of(stone), content.items());
}
@Test
void handlesNullItemsList() {
ParcelContent content = new ParcelContent(UUID.randomUUID(), null);
assertEquals(List.of(), content.items());
}|
@claude review |
…ent.java Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Adds a case verifying the compact constructor defaults a null items list to an empty list, closing the coverage gap on the defensive guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R4YqhTV42FvN95HanC7XJc
A parcel description may legitimately be null (SendingGui stores a blank description as null). AdminParcelEditGui.button() passed the value straight to String.replace(), which throws NPE when the replacement is null, crashing the admin edit GUI on InventoryClickEvent. Coerce null placeholder values to an empty string via a testable nullToEmpty() helper and cover it with unit tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Egi4whB4fVKQL3XH4PpkX9
Problem
Closes #221. Opening the parcel collection GUI threw a
NullPointerException:CollectionGui.createParcelItemAsynciterates a parcel's stored content list and callsitemStack.getType()with no null guard. The list contained a literalnullelement.Root cause
The content list survives a serialize → DB → deserialize round-trip through
ItemStackPersister, which was configured with legacy (Spigot map) ItemStack serialization. Legacy serialization drops empty/air stacks tonull. Empty/air inventory slots can slip past the GUI write filters (if (item == null) continue;does not catchisEmpty()/air), get persisted, and come back asnulllist elements — which then NPE on read.Fix
Two layers:
ParcelContent): the record's compact constructor now stripsnullandisEmpty()stacks and exposes an immutable list. Every read and write path funnels throughParcelContent, so this heals already-corrupted rows on read and prevents bad writes.ItemStackPersister): stop using legacy serialization. The jackson-bukkit Paper module defaults to an NBT byte-array format (ItemStack#serializeAsBytes) that round-trips empties safely. Its deserializer auto-detects the format, so previously stored legacy-format rows still read correctly and are rewritten in the new format on next save. This is the configuration the library docs explicitly recommend for paper plugins ("make sure that you are not using legacy serialization").Tests
ParcelContentTestcovers null-stripping and empty-stripping (written test-first, watched fail, then fixed).ItemStacks cannot be constructed in plain unit tests (they need a running server'sRegistryAccess), soItemStackis mocked.Verification notes
./gradlew compileJava testpasses. The serialization round-trip itself is not covered by automated tests: paper-api ItemStacks require a running server and the existing integration tests are Docker-gated, so the format change relies on the library's documented backward-compatible auto-detection. Worth a manual smoke test (send a parcel, collect it) on a real server before release.🤖 Generated with Claude Code