Reading Stacktraces

An unexpected error has occurred.

When your robot code hits an unexpected error, you will see this message show up in some console output (Driver Station or RioLog). You’ll probably also notice your robot abruptly stop, or possibly never move. These unexpected errors are called unhandled exceptions.

When an unhandled exception occurs, it means that your code has one or more bugs which need to be fixed.

This article will explore some of the tools and techniques involved in finding and fixing those bugs.

What’s a “Stack Trace”?

The unexpected error has occurred message is a signal that a stack trace has been printed out.

In Java and C++, a stack data structure is used to store information about which function or method is currently being executed.

A stack trace prints information about what was on this stack when the unhandled exception occurred. This points you to the lines of code which were running just before the problem happened. While it doesn’t always point you to the exact root cause of your issue, it’s usually the best place to start looking.

What’s an “Unhandled Exception”?

An unrecoverable error is any condition which arises where the processor cannot continue executing code. It almost always implies that, even though the code compiled and started running, it no longer makes sense for execution to continue.

In almost all cases, the root cause of an unhandled exception is code that isn’t correctly implemented. It almost never implies that any hardware has malfunctioned.

So How Do I Fix My Issue?

Read the Stack Trace

To start, search above the unexpected error has occurred for the stack trace.

In Java, it should look something like this:

Error at frc.robot.Robot.robotInit(Robot.java:24): Unhandled exception: java.lang.NullPointerException
         at frc.robot.Robot.robotInit(Robot.java:24)
         at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94)
         at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335)
         at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387)
         at java.base/java.lang.Thread.run(Thread.java:834)

There’s a few important things to pick out of here:

  • There was an Error

  • The error was due to an Unhandled exception

  • The exception was a java.lang.NullPointerException

  • The error happened while running line 24 inside of Robot.java

    • robotInit was the name of the method executing when the error happened.

  • robotInit is a function in the frc.robot.Robot package (AKA, your team’s code)

  • robotInit was called from a number of functions from the edu.wpi.first.wpilibj package (AKA, the WPILib libraries)

The list of indented lines starting with the word at represent the state of the stack at the time the error happened. Each line represents one method, which was called by the method right below it.

For example, If the error happened deep inside your codebase, you might see more entries on the stack:

Error at frc.robot.Robot.buggyMethod(TooManyBugs.java:1138): Unhandled exception: java.lang.NullPointerException
         at frc.robot.Robot.buggyMethod(TooManyBugs.java:1138)
         at frc.robot.Robot.barInit(Bar.java:21)
         at frc.robot.Robot.fooInit(Foo.java:34)
         at frc.robot.Robot.robotInit(Robot.java:24)
         at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94)
         at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335)
         at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387)
         at java.base/java.lang.Thread.run(Thread.java:834)

In this case: robotInit called fooInit, which in turn called barInit, which in turn called buggyMethod. Then, during the execution of buggyMethod, the NullPointerException occurred.

Perform Code Analysis

Once you’ve found the stack trace, and found the lines of code which are triggering the unhandled exception, you can start the process of determining root cause.

Often, just looking in (or near) the problematic location in code will be fruitful. You may notice things you forgot, or lines which don’t match an example you’re referencing.

Note

Developers who have lots of experience working with code will often have more luck looking at code than newer folks. That’s ok, don’t be discouraged! The experience will come with time.

A key strategy for analyzing code is to ask the following questions:

  • When was the last time the code “worked” (IE, didn’t have this particular error)?

  • What has changed in the code between the last working version, and now?

Frequent testing and careful code changes help make this particular strategy more effective.

Run the Single Step Debugger

Sometimes, just looking at code isn’t enough to spot the issue. The single-step debugger is a great option in this case - it allows you to inspect the series of events leading up to the unhandled exception.

Search for More Information

Google is a phenomenal resource for understanding the root cause of errors. Searches involving the programming language and the name of the exception will often yield good results on more explanations for what the error means, how it comes about, and potential fixes.

Seeking Outside Help

If all else fails, you can seek out advice and help from others (both in-person and online). When working with folks who aren’t familiar with your codebase, it’s very important to provide the following information:

  • Access to your source code, (EX: on github.com)

  • The full text of the error, including the full stack trace.

Common Examples & Patterns

There are a number of common issues which result in runtime exceptions.

Null Pointers and References

Both C++ and Java have the concept of “null” - they use it to indicate something which has not yet been initialized, and does not refer to anything meaningful.

Manipulating a “null” reference will produce a runtime error.

For example, consider the following code:

19  PWMSparkMax armMotorCtrl;
20
21  @Override
22  public void robotInit() {
23      armMotorCtrl.setInverted(true);
24  }

When run, you’ll see output that looks like this:

********** Robot program starting **********
Error at frc.robot.Robot.robotInit(Robot.java:23): Unhandled exception: java.lang.NullPointerException
        at frc.robot.Robot.robotInit(Robot.java:23)
        at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94)
        at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335)
        at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387)
        at java.base/java.lang.Thread.run(Thread.java:834)

Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:350): unexpected error has occurred, but yours did!
Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:352): The startCompetition() method (or methods called by it) should have handled the exception above.

Reading the stack trace, you can see that the issue happened inside of the robotInit() function, on line 23, and the exception involved “Null Pointer”.

By going to line 23, you can see there is only one thing which could be null - armMotorCtrl. Looking further up, you can see that the armMotorCtrl object is declared, but never instantiated.

Alternatively, you can step through lines of code with the single step debugger, and stop when you hit line 23. Inspecting the armMotorCtrl object at that point would show that it is null.

Fixing Null Object Issues

Generally, you will want to ensure each reference has been initialized before using it. In this case, there is a missing line of code to instantiate the armMotorCtrl before calling the setInverted() method.

A functional implementation could look like this:

19  PWMSparkMax armMotorCtrl;
20
21  @Override
22  public void robotInit() {
23      armMotorCtrl = new PWMSparkMax(0);
24      armMotorCtrl.setInverted(true);
25  }

Divide by Zero

It is not generally possible to divide an integer by zero, and expect reasonable results. Most processors (including the roboRIO) will raise an Unhandled Exception.

For example, consider the following code:

18  int armLengthRatio;
19  int elbowToWrist_in = 39;
20  int shoulderToElbow_in = 0; //TODO
21
22  @Override
23  public void robotInit() {
24     armLengthRatio = elbowToWrist_in / shoulderToElbow_in;
25  }

When run, you’ll see output that looks like this:

********** Robot program starting **********
Error at frc.robot.Robot.robotInit(Robot.java:24): Unhandled exception: java.lang.ArithmeticException: / by zero
      at frc.robot.Robot.robotInit(Robot.java:24)
      at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94)
      at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335)
      at edu.wpi.first.wpilibj.RobotBase.lambda$startRobot$0(RobotBase.java:387)
      at java.base/java.lang.Thread.run(Thread.java:834)

Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:350): unexpected error has occurred, but yours did!
Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:352): The startCompetition() method (or methods called by it) should have handled the exception above.

Looking at the stack trace, we can see a java.lang.ArithmeticException: / by zero exception has occurred on line 24. If you look at the two variables which are used on the right-hand side of the = operator, you might notice one of them has been initialized to zero. Looks like someone forgot to update it! Furthermore, the zero-value variable is used in the denominator of a division operation. Hence, the divide by zero error happens.

Alternatively, by running the single-step debugger and stopping on line 24, you could inspect the value of all variables to discover shoulderToElbow_in has a value of 0.

Fixing Divide By Zero Issues

Divide By Zero issues can be fixed in a number of ways. It’s important to start by thinking about what a zero in the denominator of your calculation _means_. Is it plausible? Why did it happen in the particular case you saw?

Sometimes, you just need to use a different number other than 0.

A functional implementation could look like this:

18  int armLengthRatio;
19  int elbowToWrist_in = 39;
20  int shoulderToElbow_in = 3;
21
22  @Override
23  public void robotInit() {
24
25     armLengthRatio = elbowToWrist_in / shoulderToElbow_in;
26
27  }

Alternatively, if zero is a valid value, adding if/else statements around the calculation can help you define alternate behavior to avoid making the processor perform a division by zero.

Finally, changing variable types to be float or double can help you get around the issue - floating-point numbers have special values like NaN to represent the results of a divide-by-zero operation. However, you may still have to handle this in code which consumes that calculation’s value.

HAL Resource Already Allocated

A very common FRC-specific error occurs when the code attempts to put two hardware-related on the same HAL resource (usually, roboRIO IO pin.)

For example, consider the following code:

19  PWMSparkMax leftFrontMotor;
20  PWMSparkMax leftRearMotor;
21
22  @Override
23  public void robotInit() {
24     leftFrontMotor = new PWMSparkMax(0);
25     leftRearMotor = new PWMSparkMax(0);
26  }

When run, you’ll see output that looks like this:

********** Robot program starting **********
Error at frc.robot.Robot.robotInit(Robot.java:25): Unhandled exception: edu.wpi.first.hal.util.UncleanStatusException:  Code: -1029. HAL: Resource already allocated
      at edu.wpi.first.hal.PWMJNI.initializePWMPort(Native Method)
      at edu.wpi.first.wpilibj.PWM.<init>(PWM.java:51)
      at edu.wpi.first.wpilibj.PWMSpeedController.<init>(PWMSpeedController.java:20)
      at edu.wpi.first.wpilibj.PWMSparkMax.<init>(PWMSparkMax.java:31)
      at frc.robot.Robot.robotInit(Robot.java:25)
      at edu.wpi.first.wpilibj.TimedRobot.startCompetition(TimedRobot.java:94)
      at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:335)
      at edu.wpi.first.wpilibj.RobotBase.startRobot(RobotBase.java:407)
      at frc.robot.Main.main(Main.java:23)

Warning at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:350): unexpected error has occurred, but yours did!
Error at edu.wpi.first.wpilibj.RobotBase.runRobot(RobotBase.java:352): The startCompetition() method (or methods called by it) should have handled the exception above.

This stack trace shows that a``edu.wpi.first.hal.util.UncleanStatusException`` has occurred. It also gives the helpful message: HAL: Resource already allocated.

Looking at our stack trace, we see that the error actually happened deep within WPILib. However, we should start by looking in our own code. Halfway through the stack trace, you can find a reference to the last line of the team’s robot code that called into WPILib: Robot.java:25.

Taking a peek at the code, we see line 25 is where the second motor controller is declared. We can also note that both motor controllers are assigned to PWM output 0. This doesn’t make logical sense, and isn’t physically possible. Therefore, WPILib purposefully generates a custom error message and exception to alert the software developers of a non-achievable hardware configuration.

Fixing HAL Resource Already Allocated Issues

HAL: Resource already allocated are some of the most straightforward errors to fix. Just spend a bit of time looking at the electrical wiring on the robot, and compare that to what’s in code.

In the example, the left motor controllers are plugged into PWM ports 0 and 1. Therefore, corrected code would look like this:

19  PWMSparkMax leftFrontMotor;
20  PWMSparkMax leftRearMotor;
21
22  @Override
23  public void robotInit() {
24
25     leftFrontMotor = new PWMSparkMax(0);
26     leftRearMotor = new PWMSparkMax(1);
27
28  }

gradlew is not recognized…

gradlew is not recognized as an internal or external command is a common error that can occur when the project or directory that you are currently in does not contain a gradlew file. This usually occurs when you open the wrong directory.

Image containing that the left-hand VS Code sidebar does not contain gradlew

In the above screenshot, you can see that the left-hand sidebar does not contain many files. At a minimum, VS Code needs a couple of files to properly build and deploy your project.

  • gradlew

  • build.gradle

  • gradlew.bat

If you do not see any one of the above files in your project directory, then you have two possible causes.

  • A corrupt or bad project.

  • You are in the wrong directory.

Fixing gradlew is not recognized…

gradlew is not recognized... is a fairly easy problem to fix. First identify the problem source:

Are you in the wrong directory? - Verify that the project directory is the correct directory and open this.

Is your project missing essential files? - This issue is more complex to solve. The recommended solution is to recreate your project and manually copy necessary code in.