Skip to content

Fire purge IslandEvents on the main thread - #3077

Merged
tastybento merged 1 commit into
developfrom
fix/purge-async-island-event
Sep 6, 2026
Merged

tastybento merged 1 commit into
developfrom
fix/purge-async-island-event

Conversation

@tastybento

Copy link
Copy Markdown
Member

Problem

Reported on Discord by a server admin running BentoBox 3.22.3 on Paper 26.2: /sgadmin purge confirm fails with

java.lang.IllegalStateException: IslandEvent may only be triggered synchronously.
        at io.papermc.paper.plugin.manager.PaperEventManager.callEvent(PaperEventManager.java:42)
        at world.bentobox.bentobox.api.events.island.IslandEvent$IslandEventBuilder.build(IslandEvent.java:410)
        at world.bentobox.bentobox.managers.PurgeRegionsService.delete(PurgeRegionsService.java:304)
        at world.bentobox.bentobox.api.commands.admin.purge.AbstractPurgeCommand.lambda$deleteEverything$0(AbstractPurgeCommand.java:89)
        at org.bukkit.craftbukkit.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:57)

Logs: https://mclo.gs/uGuzB8F, https://mclo.gs/rSn4KGX

PurgeRegionsService.delete() is dispatched from an async task by both AbstractPurgeCommand.deleteEverything() and HousekeepingManager, but it fired the DELETED / PURGED IslandEvents straight from that thread. Paper enforces that synchronous events are only fired on the main thread. Because the region files are deleted before the island loop, the crash left the region files gone but the island DB rows and cache entries intact, and the command reported the purge as failed.

Fix

delete() is now two phases:

  1. Calling (async) thread – region file deletion, residual-region check, orphaned player-data cleanup. Islands that are fully reaped are collected.
  2. Main threadfinalizeIslands() fires DELETED (if the island was never soft-deleted) and PURGED, and removes the island from the cache and database. This is scheduled with runTask and awaited via a CompletableFuture, so the boolean return and the "Purge complete" summary log still reflect the final state. When already on the main thread it runs inline.

The deleted sweep (days == 0) still defers DB rows to shutdown and never touches the scheduler.

Tests

  • testDeleteOffMainThreadFinalizesViaScheduler – with isPrimaryThread() false, verifies finalization goes through the scheduler, the cache/DB are cleaned, and DELETED then PURGED fire in order.
  • testDeleteOffMainThreadDeletedSweepDoesNotSchedule – deleted sweep off-thread never schedules or fires events.
  • Existing PurgeRegionsServiceTest cases now stub isPrimaryThread() true to keep exercising the inline path.

Full suite: 3484 tests, 0 failures.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XZRwXXfTUJLJFwSnzSgeRb

PurgeRegionsService.delete() is dispatched from an async task by both the
admin purge command and the housekeeping auto-purge, but it fired
IslandEvent (DELETED / PURGED) directly from that thread. Paper rejects
synchronous events fired off the main thread, so the age sweep crashed with
"IslandEvent may only be triggered synchronously" after the region files
had already been deleted, leaving island DB rows orphaned and reporting the
purge as failed.

Split delete() into two phases: disk I/O (region files, residual check,
player data) stays on the calling thread, and the event firing plus
cache/database removal is hopped onto the main thread via the scheduler
and awaited, so the return value and summary log still reflect the final
state. When already on the main thread the finalization runs inline.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZRwXXfTUJLJFwSnzSgeRb
@sonarqubecloud

sonarqubecloud Bot commented Sep 6, 2026

Copy link
Copy Markdown

@tastybento
tastybento merged commit bcefd95 into develop Sep 6, 2026
3 checks passed
@tastybento
tastybento deleted the fix/purge-async-island-event branch September 6, 2026 21:28
@tastybento tastybento mentioned this pull request Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant