Skip to content

Addon Troubleshooting

Leaf26 edited this page Jun 17, 2026 · 1 revision

When an addon "does nothing", the cause is almost always one of a handful of well-known mistakes: the loader never saw the jar, the ServiceLoader entry is missing, code ran before core was ready, or work was scheduled on the wrong thread. This page is the checklist. Work top to bottom; each symptom lists the fastest way to confirm and fix it.

!!! tip "First, confirm LeafRTP even tried to load your addon" On startup LeafRTP logs an [ADDONS] line listing every addon it discovered. If your addon is not in that line, the problem is discovery (sections 1-3 below), not your code. If it is listed but does nothing, the problem is lifecycle or threading (sections 4-6).

1. No [ADDONS] line / my addon is not listed

Confirm: search the server log for [ADDONS].

Likely causes, in order:

Cause Fix
Jar is in neither scanned location Drop the addon jar into the LeafRTP-owned plugins/RTP/addons/ folder or the server's plugins/ folder (Fabric/NeoForge: the platform's RTP config dir). Both are scanned. See Addon loading.
addons/ folder missing/empty and jar not in plugins/ Create plugins/RTP/addons/ and drop the jar in (or place it in plugins/). A missing or empty addons/ folder is a silent no-op by design.
Missing META-INF/services entry Ship META-INF/services/io.github.dailystruggle.rtp.api.addon.RTPAddon containing your implementation's fully-qualified class name. Without it ServiceLoader finds nothing.
Service file points at the wrong class The single line must be the exact FQN of your RTPAddon implementation, no .class, no trailing junk.
Annotation processor stripped the service file If you generate the file, confirm it survived the shade/jar step (`jar tf youraddon.jar

2. "ServiceLoader found it but it failed to instantiate"

Confirm: look for a ServiceConfigurationError or a stack trace next to the [ADDONS] line.

Likely causes:

  • Your RTPAddon has no public no-arg constructor. ServiceLoader needs one.
  • The class is abstract, non-public, or a non-static inner class.
  • A class it references at construction time is missing at runtime (you compiled against something you did not also ship or compileOnly).

3. "It loads on Paper but not on Fabric/NeoForge" (or vice versa)

  • The ServiceLoader SPI is platform-neutral and works everywhere - if it works on one platform and not another, the difference is placement, not code. Confirm the jar is in the correct per-platform location (see the Addon loading table).
  • If your addon touches org.bukkit.* types directly it will NoClassDefFoundError on Fabric/NeoForge. Keep platform code out of the addon, or guard it behind a platform check and route through RTP.serverAccessor.

4. "It loads, but my registration never takes effect"

The number-one cause: you registered too early.

  • Do all registration from RTPAddon.onLoad(), not from a static initializer, a field initializer, or your own JavaPlugin constructor. Contract-tier entry points throw IllegalStateException (S-006) when called before core is ready - they do not silently no-op, so check the log for that exception.
  • A config parser registered with RTP.configs.putParser(...) must be re-registered on reload: hook Configs.onReload(...), or your file will work once and vanish after /rtp reload.
  • A verifier registered via RTPAPI.hooks().verifiers().register(...) only affects future selections. Locations already in the cache were verified before your hook existed; clear the cache (or /rtp scan reset) to re-verify with the new rule.

5. "Random freezes / ThreadAccessException / chunk errors after I added the addon"

This is a threading-contract violation. The engine is strict about it on purpose.

Symptom Cause Fix
ThreadAccessException (Folia) You touched world/player state from the wrong region thread, or used a raw Thread/Executors. Schedule through RTP.scheduler (it maps to the right thread per platform). Never spawn your own threads in addon code.
Server stalls during /rtp Your verifier or callback blocked, or did main-thread chunk I/O. Verifiers run async and must not block or load chunks on the main thread (S-005). Make them pure/cheap.
Teleports silently stop happening Your verifier or callback swallowed an exception. Never catch-and-ignore (S-004). Let it propagate or log via RTP.log(...).
State touched off-tick after a network call You used a NetworkTransport future result directly. Those futures complete on a transport-owned executor; hop back via RTP.scheduler before touching players. See Cross-server RTP for addons.

6. "teleport(...) returns but the player doesn't move"

  • Read the RTPResult you got back - it is never a silent no-op. It tells you whether the request succeeded, was queued (a coordinate is being generated; the player teleports when it is ready), or was rejected with a reason (cooldown, cost, permission, no valid destination). Surface that reason to the player instead of assuming failure.
  • If results are always "queued" and never complete, the region's cache is empty and generation is not keeping up - run /rtp scan and check performance.yml.

7. "My menu rows are empty / wrong"

  • getAllowedTargets(player) is permission-filtered. Empty list means the player lacks the region/world permissions, not that the API is broken.
  • getTargetStatus(...) reflects live cooldown/cost; if it looks stale, you are caching it - re-query per open.
  • Remote (cross-server) rows only exist when a NetworkTransport binding is installed; with none, fall back to local targets. See Cross-server RTP for addons.

Still stuck?

  • Capture the full startup log (the [ADDONS] line and any stack trace) and /rtp info output.
  • Confirm your build pins a release tag and uses compileOnly/provided (not implementation) - see API stability.
  • Compare against the working reference: Example addon.

See also: Addon development, Addon loading, Hooks and verifiers.

Clone this wiki locally