BlazeBans API/Reference

Common mistakes

The errors that come up most, and what actually causes them.

The server freezes when my plugin runs

You blocked on a future.

java
// This stops the whole server until the database answers.
boolean banned = api.punishments().isBanned(name, uuid).join();

Never call join(), get(), or getNow() on a server thread. Chain a callback instead. Full explanation in Threading.

This one hides in testing, because a local SQLite lookup takes a millisecond. It surfaces on the production network with the database on another host.

IllegalStateException: BlazeBans API is not available yet

BlazeBansProvider.get() was called before BlazeBans registered.

Three causes, in order of likelihood:

No depend entry. Without depend: [BlazeBans] in your plugin.yml, load order is not guaranteed.

hooks.api: false. BlazeBans enables normally but never registers the API. Check plugins/BlazeBans/settings.yml.

Called too early. onLoad runs before other plugins enable. Get the API in onEnable.

Handle it with a clear message rather than a stack trace:

java
this.blazeBans = BlazeBansProvider.optional().orElse(null);
if (this.blazeBans == null) {
    getLogger().severe("The BlazeBans API is unavailable. Check hooks.api in settings.yml.");
    getServer().getPluginManager().disablePlugin(this);
    return;
}

My plugin will not load at all

If the error mentions duplicate or already-defined classes, you bundled the API.

The API must be compileOnly in Gradle or provided in Maven. BlazeBans supplies these classes at runtime, and a second copy in your jar breaks the load. If you use the Shadow plugin, check the API is not in a configuration it shades.

Cancelling the created event does not stop the ban

It is not supposed to.

BlazeBansPunishmentCreatedEvent fires after the record is stored. Cancelling it suppresses the follow-up: removing the player, the broadcast, the Discord post. The punishment still exists and still applies at the next login.

To stop a punishment happening, cancel BlazeBansPunishmentCreateEvent, without the d. The two names are one letter apart and this is the most common mix-up in the whole API.

EventCancelling
...PunishmentCreateEventNothing is stored
...PunishmentCreatedEventStored anyway, follow-up suppressed

Cancelling the muted event blocks the command

Backwards from what you expect, and correct.

These events fire when BlazeBans is about to block something. Cancelling the BlazeBans event means "do not block it", so cancelling is the exemption.

java
@EventHandler
public void onMutedCommand(BlazeBansMutedCommandEvent event) {
    if (event.command().startsWith("/appeal")) {
        event.setCancelled(true);  // allows /appeal through
    }
}

Leaving it uncancelled is what blocks it.

NullPointerException on a punishment field

Five fields on Punishment are nullable: targetUuid, staffUuid, expiresAt, revokedAt, and the revoke strings.

java
// Throws when the punishment is permanent.
Duration remaining = Duration.between(Instant.now(), punishment.expiresAt());

// Correct.
if (!punishment.permanent()) {
    Duration remaining = Duration.between(Instant.now(), punishment.expiresAt());
}

staffUuid is null for console and automated punishments. targetUuid is null on records imported from another plugin that stored names only.

proofUrl, serverScope, and serverId are never null. They are blank strings when absent.

Async thread cannot access the world

You touched Bukkit from a future callback.

java
// Wrong.
api.punishments().isBanned(name, uuid)
    .thenAccept(banned -> player.kick(Component.text("Banned")));

// Right.
api.punishments().isBanned(name, uuid)
    .thenAccept(banned -> {
        if (!banned) {
            return;
        }
        player.getScheduler().run(plugin, task ->
            player.kick(Component.text("Banned")), null);
    });

The same applies inside BlazeBansMutedChatEvent and BlazeBansMutedCommandEvent, which fire asynchronously. Check event.isAsynchronous().

UnsupportedOperationException on Folia

You used Bukkit.getScheduler().

The legacy BukkitScheduler has no meaning on Folia. Use the schedulers that work on both:

TargetScheduler
A player or entityentity.getScheduler()
A location or chunkBukkit.getRegionScheduler()
Server-wide stateBukkit.getGlobalRegionScheduler()

Nothing happens and there is no error

An exception completed the future and nobody handled it.

java
api.punishments().isBanned(name, uuid)
    .thenAccept(banned -> { /* never runs */ });
    // A storage failure here is swallowed silently.

Always terminate a chain with exceptionally or whenComplete:

java
api.punishments().isBanned(name, uuid)
    .thenAccept(banned -> { /* ... */ })
    .exceptionally(error -> {
        getLogger().warning("BlazeBans lookup failed: " + error.getMessage());
        return null;
    });

My alt lookup always returns nothing

IP storage is off. It is off by default:

yaml
privacy:
  store-ip-addresses: true

Your plugin cannot enable this and should not assume it is enabled. Also note that players on the alt blacklist and holders of blazebans.alts.exempt are excluded either way.

isBanned says false but the player is banned

The punishment is scoped to a different server.

isBanned, isMuted, activeBan, and activeMute only consider punishments applying to this server, which is what enforcement does too. history() is not filtered, so a record showing there but not in isBanned is a scope difference.

See Servers and scope.

My punishment bypassed permissions

createPunishment is not a command. It skips staff hierarchy, duration limits, scope permissions, and exemptions, behaving like a console punishment.

If your plugin punishes on behalf of a player, deciding whether that player was allowed to is your job.

Duration.ZERO made it permanent

That is what it means. Zero, negative, and null durations all create a permanent punishment.

java
Duration.ofHours(2)   // two hours
Duration.ZERO         // permanent
null                  // permanent

There is no "zero-length punishment". If you compute a duration that might land on zero, guard it before you build the request.

My addon does not show in /blazebans addons

Three possibilities:

You never registered it. BlazeBansProvider.registerAddon(...) in onEnable.

Your plugin failed to enable. Check console for an earlier error.

You registered before disabling yourself. If you disable the plugin on a failed startup, register with a status explaining why first, so the operator sees the reason.

Reaching into BlazeBans internals

java
// Will not compile against the API jar, and would break every release anyway.
net.blazebans.common.punishment.PunishmentService service = ...;

Everything outside net.blazebans.api is obfuscated and repackaged with names that change every release. The API package is the whole supported surface, and its names are frozen precisely so you can rely on them.

If something you need is not exposed, dispatching the console command is the supported workaround.

The API version does not tell you much

java
if (api.version().startsWith("1.0")) { /* ... */ }

You compiled against a fixed API, so a method that exists at compile time exists at runtime. Version strings are for logging.

If you genuinely need to support an older BlazeBans that lacks something, catch NoSuchMethodError around the call rather than parsing versions.

Still stuck

The BlazeStudios Discord. Bring the code, the full stack trace, and the output of /blazebans version.