Anuken/Mindustry · warning · ValidateException
Player attempted to control invalid unit.
Error message
Player attempted to control invalid unit.
What it means
Thrown server-side as ValidateException in the else-branch of unitControl: the requested unit was not a valid possession target (not AI, dead, or wrong team). Even though the earlier possession/admin checks passed, the specific unit failed the runtime eligibility test (unit.isAI() && team match && !dead && playerControllable). This catches clients attempting to control units they should not.
Source
Thrown at core/src/mindustry/input/InputHandler.java:814
if(before.spawnedByCore){
unit.dockedType = before.type;
}else if(before.dockedType != null && before.dockedType.coreUnitDock){
//direct dock transfer???
unit.dockedType = before.dockedType;
}
if(before.spawnedByCore && !before.isPlayer()){
Call.unitDespawn(before);
}
}
Time.run(Fx.unitSpirit.lifetime, () -> Fx.unitControl.at(unit.x, unit.y, 0f, unit));
if(!player.dead()){
Fx.unitSpirit.at(player.x, player.y, 0f, unit);
}
}else if(net.server()){
//reject forwarding the packet if the unit was dead, AI or team
throw new ValidateException(player, "Player attempted to control invalid unit.");
}
Events.fire(new UnitControlEvent(player, unit));
}
@Remote(targets = Loc.both, called = Loc.server, forward = true)
public static void unitClear(Player player){
if(player == null) return;
//make sure player is allowed to control the building
if(net.server() && !netServer.admins.allowAction(player, ActionType.respawn, action -> {})){
throw new ValidateException(player, "Player cannot respawn.");
}
if(!player.dead() && !player.unit().spawnedByCore){
var docked = player.unit().dockedType;
//get best core unit type as approximationView on GitHub (pinned to f695ad7e60)
Solutions
- Server: treat ValidateException here as expected anti-cheat rejection; log and continue, do not crash.
- Ensure modded units correctly implement isAI()/playerControllable()/team semantics.
- Clients should only attempt control of valid AI allied units.
- If race conditions cause false positives, re-validate unit liveness client-side before requesting.
Example fix
// before: client requests any unit
Call.unitControl(player, unit);
// after: client validates target
if(unit.isAI() && !unit.dead && unit.team == player.team() && unit.playerControllable()){
Call.unitControl(player, unit);
} Defensive patterns
Strategy: try-catch
Validate before calling
// client validates the target unit before requesting control
if(unit == null || !unit.isAI() || unit.dead || unit.team != player.team() || !unit.playerControllable()){
return; // server would reject this
} Try / catch
try{ Call.unitControl(player, unit); }catch(mindustry.net.ValidateException e){ Log.debug("rejected invalid unit control from @", e.player); } Prevention
- Clients: only request control of living AI allied playerControllable units.
- Modded units: implement isAI/playerControllable/team correctly.
- Servers: treat this ValidateException as anti-cheat rejection, not an error.
- Re-check unit liveness right before sending to avoid races.
When it happens
Trigger: Client sends unitControl for a unit that is dead, not AI-controlled, on a different team, or not player-controllable. The server reaches the final else and throws. Only on net.server().
Common situations: Malicious client forging a control packet for an enemy/dead/non-AI unit; race where the unit died or changed team between request and validation; modded units that incorrectly report playerControllable.
Related errors
- Player cannot configure a tile.
- Player cannot control a building.
- Player cannot control a unit.
- Player cannot respawn.
- Map has no cores!
AI-assisted analysis of Anuken/Mindustry@f695ad7e60 (2026-08-14).
Data as JSON: /api/errors/064dd1883c064bcf.
Report an issue: GitHub.