Description
Describe the bug
A clear and concise description of what the bug is.
Error at frc.robot.TestBehaviors.bindButtons(TestBehaviors.java:85): Unhandled exception: java.util.ConcurrentModificationException: Cannot bind EventLoop while it is running
at edu.wpi.first.wpilibj.event.EventLoop.bind(EventLoop.java:28)
at edu.wpi.first.wpilibj2.command.button.Trigger.addBinding(Trigger.java:70)
at edu.wpi.first.wpilibj2.command.button.Trigger.onTrue(Trigger.java:110)
at frc.robot.TestBehaviors.bindButtons(TestBehaviors.java:85)
at edu.wpi.first.wpilibj2.command.FunctionalCommand.initialize(FunctionalCommand.java:52)
at edu.wpi.first.wpilibj2.command.WrapperCommand.initialize(WrapperCommand.java:38)
at edu.wpi.first.wpilibj2.command.CommandScheduler.initCommand(CommandScheduler.java:169)
at edu.wpi.first.wpilibj2.command.CommandScheduler.schedule(CommandScheduler.java:212)
at edu.wpi.first.wpilibj2.command.CommandScheduler.schedule(CommandScheduler.java:243)
at edu.wpi.first.wpilibj2.command.Command.schedule(Command.java:538)
at edu.wpi.first.wpilibj2.command.button.Trigger.lambda$onTrue$1(Trigger.java:113)
at edu.wpi.first.wpilibj2.command.button.Trigger$1.run(Trigger.java:78)
at java.base/java.lang.Iterable.forEach(Iterable.java:75)
at edu.wpi.first.wpilibj.event.EventLoop.poll(EventLoop.java:38)
at edu.wpi.first.wpilibj2.command.CommandScheduler.run(CommandScheduler.java:280)
at frc.robot.Robot.robotPeriodic(Robot.java:24)
at edu.wpi.first.wpilibj.IterativeRobotBase.loopFunc(IterativeRobotBase.java:400)
at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:133)
at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:419)
at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$1(RobotBase.java:490)
at java.base/java.lang.Thread.run(Thread.java:840)
arising from this code:
...
// var bindCommand(Commands.run(this::bindButtons).until(true));
var bindCommand = Commands.runOnce(this::bindButtons);
new Trigger(m_testerController.getHID()::isConnected)
.onTrue(bindCommand.ignoringDisable(true));
...
private void bindButtons() {
if (m_buttonsBound || !m_testerController.getHID().isConnected()) return;
m_buttonsBound = true;
// the following line is frc.robot.TestBehaviors.bindButtons(TestBehaviors.java:85) in the traceback above
m_testerController.start().onTrue(Commands.runOnce(m_coralSim::loadCoral).withName("LoadSimCoral"));
m_testerController.rightBumper().whileTrue(new IntakeCommand(m_intake, m_elevator));
}
when m_testerController
is connected causing bindButtons()
to be run.
Expected behavior
The bindButtons
method binds the buttons without incurring an exception.
Desktop (please complete the following information):
- OS: Linux 22.04 (simulation). The nature of the behavior suggests that it would be the same on the roboRIO or any other desktop.
- Project Information:
- WPILib Information:
Project Version: 2025.2.1
VS Code Version: 1.94.2
WPILib Extension Version: 2025.2.1
C++ Extension Version: 1.23.2
Java Extension Version: 1.38.0
Java Debug Extension Version: 0.58.2024090204
Java Dependencies Extension Version 0.24.1
Java Version: 17
Java Location: /home/hauser/wpilib/2025/jdk
Vendor Libraries:
PathplannerLib (2025.2.1)
CTRE-Phoenix (v5) (5.35.1)
CTRE-Phoenix (v6) (25.2.1)
REVLib (2025.0.2)
ReduxLib (2025.0.1)
Studica (2025.0.1)
ThriftyLib (2025.0.1)
WPILib-New-Commands (1.0.0)
maplesim (0.3.1)
photonlib (v2025.1.1)
YAGSL (2025.2.2)
Additional context
For a couple of seasons I've had this as var bindCommand = Commands.run(this::bindButtons)
. It has worked fine, but this year I noticed that the command was left running after doing its job -- it should have been runOnce
. But on changing it to runOnce
it suffered the ConcurrentModificationException
as shown above.
I think that the problem is that when a Command is scheduled from a Trigger, the Command's start
method is called from the Trigger processing loop; and Commands.runOnce
places the method to be run as the start
method of the Command that it constructs. Using var bindCommand(Commands.run(this::bindButtons).until(true));
instead, the error does not happen because this::bindButtons
is placed as the execute
method of the constructed command where it is called from the Scheduler loop.
I'm not sure this needs to be fixed -- at this level of the system small changes may well affect other edge-case behaviors. But at least it is now noted and a work-around provided.