{"record":{"id":"064dd1883c064bcf","repo":"Anuken/Mindustry","slug":"player-attempted-to-control-invalid-unit","errorCode":null,"errorMessage":"Player attempted to control invalid unit.","messagePattern":"Player attempted to control invalid unit\\.","errorType":"validation","errorClass":"ValidateException","httpStatus":null,"severity":"warning","filePath":"core/src/mindustry/input/InputHandler.java","lineNumber":814,"sourceCode":"                if(before.spawnedByCore){\n                    unit.dockedType = before.type;\n                }else if(before.dockedType != null && before.dockedType.coreUnitDock){\n                    //direct dock transfer???\n                    unit.dockedType = before.dockedType;\n                }\n\n                if(before.spawnedByCore && !before.isPlayer()){\n                    Call.unitDespawn(before);\n                }\n            }\n\n            Time.run(Fx.unitSpirit.lifetime, () -> Fx.unitControl.at(unit.x, unit.y, 0f, unit));\n            if(!player.dead()){\n                Fx.unitSpirit.at(player.x, player.y, 0f, unit);\n            }\n        }else if(net.server()){\n            //reject forwarding the packet if the unit was dead, AI or team\n            throw new ValidateException(player, \"Player attempted to control invalid unit.\");\n        }\n\n        Events.fire(new UnitControlEvent(player, unit));\n    }\n\n    @Remote(targets = Loc.both, called = Loc.server, forward = true)\n    public static void unitClear(Player player){\n        if(player == null) return;\n\n        //make sure player is allowed to control the building\n        if(net.server() && !netServer.admins.allowAction(player, ActionType.respawn, action -> {})){\n            throw new ValidateException(player, \"Player cannot respawn.\");\n        }\n\n        if(!player.dead() && !player.unit().spawnedByCore){\n            var docked = player.unit().dockedType;\n\n            //get best core unit type as approximation","sourceCodeStart":796,"sourceCodeEnd":832,"githubUrl":"https://github.com/Anuken/Mindustry/blob/f695ad7e60323ebced984fa26d0bcf0bc54296b4/core/src/mindustry/input/InputHandler.java#L796-L832","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before: client requests any unit\nCall.unitControl(player, unit);\n\n// after: client validates target\nif(unit.isAI() && !unit.dead && unit.team == player.team() && unit.playerControllable()){\n    Call.unitControl(player, unit);\n}","handlingStrategy":"try-catch","validationCode":"// client validates the target unit before requesting control\nif(unit == null || !unit.isAI() || unit.dead || unit.team != player.team() || !unit.playerControllable()){\n    return; // server would reject this\n}","typeGuard":null,"tryCatchPattern":"try{ Call.unitControl(player, unit); }catch(mindustry.net.ValidateException e){ Log.debug(\"rejected invalid unit control from @\", e.player); }","preventionTips":["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."],"tags":["network","validation","multiplayer","server","anti-cheat"],"backgroundTag":null,"analyzedSha":"f695ad7e60323ebced984fa26d0bcf0bc54296b4","analyzedAt":"2026-08-14T04:31:16.262Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}