Anvil
Troubleshooting

Players and routing

Diagnose connections, capability discovery, version matching, and backend observations.

Separate creation, connection, and observation. Creating a player registers its name in the scenario; it does not log in. The Session capability starts the connection, and a bounded wait establishes whether the expected state was observed.

Check the connection target

The target of the player's PlayerConnection selects a named server or proxy. When omitted, the scenario entrypoint is used. Check the names against the scenario declaration rather than a stale handle from another run. A native client must match every server reachable through that target. Anvil does not add ViaVersion or translate the client's protocol when a topology has mixed incompatible versions.

Read the entry process's console first, then the target backend's console. Confirm that the proxy lists the intended backend and has a valid default server. See forwarding.

Check a virtual host or source address

A player with a virtualHost still connects to the target's real address; only its handshake changes. When it lands on the default backend instead of a forced host, compare the host with the proxy's forced-hosts key, which Velocity matches in lower case.

A sourceAddress is refused when the player is created, never replaced by 127.0.0.1:

MessageCheck
... must be an IP address literal such as 127.0.0.2 or ... is not a loopback addressUse a loopback literal, not a host name or LAN address
... which this machine cannot bindOn macOS, add the address as a loopback alias, for example sudo ifconfig lo0 alias 127.0.0.2
... the scenario's execution provider translates game connectionsDocker execution hides the client address from the process; use local execution
... only reaches a loopback listener of the same address familyMatch the target's address family, for example 127.0.0.2 for a 127.0.0.1 listener

Check the protocol library

Creating a player selects a protocol library and the exact release for its Minecraft version, and applies the support policy before the player exists:

MessageCheck
No installed protocol library supports Minecraft <version>Install a library that supports the version, or add release data for a newer version
Protocol libraries [...] support Minecraft <version> equallySet protocolLibrary on the player, the scenario, or the engine
Protocol library '<id>' selected for player '<name>' does not support Minecraft <version>Choose another library, or a server version the selected library lists
The STRICT support policy refuses ...The release is UNTESTED; use the default LENIENT policy or a version Anvil's data lists
MCProtocolLib release <version> has no pinned checksum for <module>Run the named pin task, or set the sha256 in your own release data
Authenticated account '<id>' is not stored by protocol library '<library>'Sign the account in through that library, or select the library that stores it

The versions page lists the releases and support levels.

Capability discovery

SymptomCheck
A capability is absentIts public API alone is insufficient; install its consumer wiring artifact
A declared dependency is unavailableInstall compatible wiring for the predecessor capabilities
A capability reports not installed by the <library> workerThe library's worker has no wiring for it: install the capability's bundle, which brings its worker sides, or select a library whose edition it supports
A capability reports a missing class, member or packetThe player's release cannot carry what it needs: a client segment does not link, or the release lacks the packet; the reason names it
The same capability has competing providersKeep one selected implementation for that type and protocol library
An external capability is ignoredCheck its service descriptor, its requiredProtocols() against the library's protocols(), and any supportedLibraries()
An agent operation is unknownInstall the extension JAR under plugins/anvil-agent-extensions and verify its operation namespace

Library selection happens before capability composition. MCProtocol-specific adapters do not become compatible with another protocol library by putting them on its classpath. See capability installation and extension packaging.

Verify the observation you need

A client connection proves a protocol session was established. To verify the backend, watch that backend's Players capability before the action and wait for the player's join, which the backend's own agent reports. To verify which backend the proxy chose, watch the proxy before the action and wait for joined(player, backend). Do not read the proxy's backend(player) straight after the action or after the backend's join: it is a snapshot of the proxy's reports, which can arrive a moment later, and can still name the previous backend. Use a backend-specific operation if you need native state inside that server. A successful reconnect by itself does not prove that your plugin restored data; assert the plugin-specific behavior after reconnecting.

Use bounded capability waits for messages, connection changes, and backend arrival. A fixed sleep only delays the test. If a wait times out, inspect the reported recent history and confirm that the action actually triggers the expected event. When old messages could satisfy an assertion, use the distinct message text for each action, and inspect the captured history as described in Messages.

For online players, verify that the selected account exists in the configured private account root used by the run. Add or import the account through Anvil's account manager; do not put tokens in scenario code or Gradle properties. See Authentication.

On this page