Command-based is powerful but has sharp edges that bite nearly every new programmer. Here are the most common ones and the fix.
Mistake 1: Default command without a requirement#
A default command must require its subsystem, or the scheduler throws an IllegalArgumentException complaining that default commands must require their subsystem. The fix is almost always to build the command from the subsystem's own factory (run, runOnce) so the requirement is added automatically:
// Wrong: a bare command that doesn't require the drivetrain
m_drive.setDefaultCommand(new RunCommand(() -> m_drive.stop()));
// Right: factory on the subsystem adds the requirement for you
m_drive.setDefaultCommand(m_drive.run(() -> m_drive.stop()));
Mistake 2: Forgetting requirements anywhere#
If two commands touch the same subsystem but only one declares the requirement, the scheduler won't know they conflict and both run -- fighting over the motor. Every command that reads or writes a subsystem must list it in addRequirements() (or be built from that subsystem's factory). Correct requirements are what let the scheduler deschedule the default command when a real command is triggered.
Mistake 3: Reusing a single command instance#
A Command object can only be scheduled in one place at a time. Storing one instance and binding it to two triggers causes confusing 'command already scheduled' behavior. Prefer command factories -- methods that return a new command each call:
public Command scoreL4() { return runOnce(() -> setGoal(L4)); }
Mistake 4: Putting logic in default commands#
Keep default commands trivial -- stop or hold position. Decision logic belongs in triggers composed with and(), or(), negate(), and smoothed with debounce(). Frame triggers as problem-domain yes/no questions (hasGamePiece, isAtSpeed), and never expose raw sensor values to RobotContainer.
Mistake 5: Comparing floats with ==#
encoder.getPosition() == 10.0 is almost never true. Use a tolerance:
if (MathUtil.isNear(10.0, encoder.getPosition(), 0.05)) { ... }
Mistake 6: Expensive work repeated per trigger#
Every trigger condition is polled each loop. If a trigger calls an expensive method (a CAN query, a vision computation), and several triggers ask the same question, you pay that cost repeatedly. Compute it once per loop in periodic(), cache it, and have triggers read the cached value.
Getting these six right eliminates the majority of 'the robot does weird things' command bugs before they ever reach the field.
the part worth keeping
Key takeaways
- Default commands must require their subsystem -- build them from the subsystem's run()/runOnce() factories.
- Every command must declare requirements so the scheduler can resolve conflicts and deschedule defaults.
- Use command factories that return new instances; never reuse one command object across bindings.
- Keep logic in debounced triggers (not default commands), use MathUtil.isNear() for floats, and cache expensive trigger values.
Programming, Controls & SensorsCommon Mistakes and Troubleshootinglesson 2 of 5
Keep going
Take the quiz+10 XP with an accountMore in Common Mistakes and Troubleshooting
where this came from
Sources and corrections
This lesson is AI-assisted: drafted from primary sources, then reviewed and edited by hand. Errors still get through. When one is reported we fix it and write down what changed, in public, in the corrections log.
sources and further reading
- bovlb.github.ioBoVLB: Command-Based Best Practices
- docs.wpilib.orgWPILib: Commands
- docs.wpilib.orgWPILib: Subsystems
clipped to this lesson
Articles that go further on this
The lesson gets you through the topic. These go wider on it, and they read in one sitting.
- 9 min readFRC Command-Based Programming: Subsystems, Commands, and the SchedulerA beginner-friendly guide to WPILib command-based programming in Java: subsystems, commands, the CommandScheduler, triggers, and composing commands./blogread it
- 13 min readFRC Code Structure Best Practices: Command-Based Project ArchitectureHow to structure an FRC command-based robot project the right way — subsystems, commands, RobotContainer, and Constants — verified against official WPILib docs./blogread it
answer sheet
Lesson quiz
All 4 right completes the lesson. Miss one and only that question comes back, anything you already answered correctly stays banked.
0 of 4 answered
01A default command throws IllegalArgumentException for missing a requirement. What is the recommended fix?
02Why should you avoid storing one command instance and binding it to two different triggers?
03Comparing a float like encoder.getPosition() == 10.0 is almost never true. What should you do instead?
04Several triggers each call an expensive CAN or vision method every loop. How should you avoid paying that cost repeatedly?
Answer every question to submit.
All 51 lessons in Programming, Controls & Sensorsopenclose
01 / prerequisites
02 / foundations-tools-and-first-program
03 / robot-program-and-command-based
04 / motors-and-control
05 / autonomous-trajectories-simulation
06 / sensing-fundamentals
07 / encoders
08 / gyros-imus-orientation
09 / closed-loop-control
10 / vision-pose-estimation
11 / worked-examples-mini-projects
- Not read yet:Mini-Project: A Closed-Loop Elevator with Motion Magic
- Not read yet:Mini-Project: A Velocity-Controlled Shooter on REVLib
- Not read yet:Mini-Project: A Teleop Swerve Drive Subsystem
- Not read yet:Mini-Project: An Autonomous Routine with PathPlanner
- Not read yet:Mini-Project: Vision-Aligned Scoring with Limelight
12 / common-mistakes-troubleshooting
13 / advanced-techniques-case-studies
- Not read yet:State-Space Control and Kalman Filtering
- Not read yet:Log Replay Architecture with AdvantageKit
- Not read yet:Advanced Pose Estimation: Multi-Tag Fusion and Standard Deviations
- Not read yet:Robot Coordination, Alerts, and Operator Feedback
- Not read yet:Case Study: Hardening Software Before an Event