{"record":{"id":"da28d09851ff61fe","repo":"Anuken/Mindustry","slug":"player-cannot-control-a-unit","errorCode":null,"errorMessage":"Player cannot control a unit.","messagePattern":"Player cannot control a unit\\.","errorType":"validation","errorClass":"ValidateException","httpStatus":null,"severity":"warning","filePath":"core/src/mindustry/input/InputHandler.java","lineNumber":777,"sourceCode":"    }\n\n    @Remote(called = Loc.server)\n    public static void unitBuildingControlSelect(Unit unit, Building build){\n        if(unit == null || unit.dead()) return;\n\n        //client skips checks to prevent ghost units\n        if(unit.team() == build.team && (net.client() || build.canControlSelect(unit))){\n            build.onControlSelect(unit);\n        }\n    }\n\n    @Remote(targets = Loc.both, called = Loc.both, forward = true)\n    public static void unitControl(Player player, @Nullable Unit unit){\n        if(player == null) return;\n\n        //make sure player is allowed to control the unit\n        if(net.server() && (!state.rules.possessionAllowed || !netServer.admins.allowAction(player, ActionType.control, action -> action.unit = unit))){\n            throw new ValidateException(player, \"Player cannot control a unit.\");\n        }\n\n        //clear player unit when they possess a core\n        if(unit == null){ //just clear the unit (is this used?)\n            player.clearUnit();\n            //make sure it's AI controlled, so players can't overwrite each other\n        }else if(unit.isAI() && unit.team == player.team() && !unit.dead && unit.playerControllable()){\n            if(net.client() && player.isLocal()){\n                player.justSwitchFrom = player.unit();\n                player.justSwitchTo = unit;\n            }\n\n            //TODO range check for docking?\n            var before = player.unit();\n\n            player.unit(unit);\n\n            if(before != null){","sourceCodeStart":759,"sourceCodeEnd":795,"githubUrl":"https://github.com/Anuken/Mindustry/blob/f695ad7e60323ebced984fa26d0bcf0bc54296b4/core/src/mindustry/input/InputHandler.java#L759-L795","documentation":"Thrown server-side as ValidateException in unitControl when possession is globally disabled (state.rules.possessionAllowed == false) or netServer.admins.allowAction(control) denies controlling the specific unit. Guards possession of AI units by players.","triggerScenarios":"Client requests unitControl(unit) while state.rules.possessionAllowed is false, or the admin layer denies the control action for that unit. Server-side throw inside the @Remote handler.","commonSituations":"Server/gamemode forbids possession but a client tries anyway; permission plugin restricts which units may be controlled; anti-cheat rejects possession of disallowed units.","solutions":["Server: catch/handle ValidateException as an expected rejection.","Clients: gate possession requests on state.rules.possessionAllowed before calling.","Verify the target unit is playerControllable() and on the player's team.","Review admin/permission rules if valid possessions are rejected."],"exampleFix":"// before\nCall.unitControl(player, unit);\n\n// after\nif(state.rules.possessionAllowed && unit.playerControllable() && unit.team == player.team()){\n    Call.unitControl(player, unit);\n}","handlingStrategy":"try-catch","validationCode":"// client gates possession request\nif(!state.rules.possessionAllowed){ return; }\nif(unit != null && (!unit.playerControllable() || unit.team != player.team())){ return; }","typeGuard":null,"tryCatchPattern":"try{ Call.unitControl(player, unit); }catch(mindustry.net.ValidateException e){ Log.debug(\"rejected unit control from @\", e.player); }","preventionTips":["Clients: check possessionAllowed, playerControllable, and team first.","Servers: catch ValidateException and continue.","Keep possession rules consistent across gamemodes.","Log denials for anti-cheat review."],"tags":["network","validation","multiplayer","server","control"],"backgroundTag":null,"analyzedSha":"f695ad7e60323ebced984fa26d0bcf0bc54296b4","analyzedAt":"2026-08-14T04:31:16.262Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}