One common frustration with the way JavaFX loads Views with the FXMLLoader is that it does not make debugging easy! “Location is not set” is a prime example of this. But, it actually surrounds how the getResource() method finds your FXML file – something that’s not specific to JavaFX.

The “Location is not set” error generated by the FXMLLoader is thrown when the getResource() method cannot locate that resource. Instead, getResource() returns a null reference, which is passed to the FXMLLoader. The FXMLLoader interprets this as a failure to set the location parameter for the FXML file.

What we’ll achieve in this tutorial

Firstly, we’ll look at how to debug the problem. Then, we’ll run through some rules and best practice to store resources sustainably in the future. Finally, well go through one example of how the IDE can create this error – even if your Java code is fine.

We’ll debug a simple application with a package structure that looks like this:

Key points are:

  • Controller and Main are both in the com.edencoding folder
  • Resources have been separated from the Java code by specifying a resources folder
  • The sample.fxml file is not in a package folder, but in the root of the resources directory.

If you hadn’t guessed, the reason you need to know the project structure is that it’s almost always the source of this problem.

Why “Location is not set” occurs

The parameter we pass to the FXMLLoader during construction is labelled ‘location’, so I usually see this error and think “but I did set it!”

But, if you look at the parameters we pass to the FXMLLoader, we don’t actually pass it a file name – or even a File object. In fact, what we pass it is a URL. And, if you’re following convention, you don’t construct the URL separately, you create it using getClass().getResource().

The first step to debugging this program is disentangling the URL that we create using getClass().getResource() from the instantiation of the FXMLLoader. Let’s split it into two lines:

URL fxmlLocation = getClass().getResource("sample.fxml");
FXMLLoader loader = new FXMLLoader(fxmlLocation);

There are two ways to look at the output of this – one is to set a breakpoint and look at the contents of fxmlLocation with debugging tools. The other is to print out the fxmlLocation variable. It doesn’t matter which one you do, it’ll have the same result (assuming the project structure above):

null

Well, drat 🤦‍♂️

So as we pass it to the FXMLLoader, it’s passed as a null reference.

Then, when the FXMLLoader tries to load the FXML file, it sees a null URL and assumes that the location parameter wasn’t set in the first place.

Inside the constructor of the FXMLLoader, there’s no check to see whether the URL is null – it just blindly sets it, allowing the problem to percolate through the program until we call fxmlLoader.load().

If you want to know more about the orderin which the FXMLLoader does things, we talk about it more in depth here.

To get the right reference, we need to find out what happened in the getResource() method – work out what went wrong – and then fix it.

Fixing a null URL reference

We’ll take three steps to fixing the “Location is not set” error:

  • Looking at the internal logic of getResource()
  • Determining the correct path to a resource
  • Fixing common resources directory issues

In the case of that third option, the location is correct and the folder structure is wrong!

We’ll go through each in turn.

1. The internal logic of getResource()

The internal logic of getResource() is well documented. In fact, the very first line of code in the getResource() method is to take the name of the file we’ve specified, and work out where that file belongs in our project.

public URL getResource(String name) {
    name = resolveName(name);
    ...
}

Inside resolveName(), there are some really simple rules for working out where in the project that file belongs.

Tip: One thing to remember when you read these is that the optional leading "/" does not have a literal file-system meaning. It’s a flag used by Java to determine where to start the search for a resource.

Could they have chosen a different flag? Sure.

Did they? Not a chance.

  1. If location given begins with “/”, navigate straight from the root of the project (or if we’ve made it into a Jar, from the root of the Jar ).

Remember that by the time this code runs, it won’t be searching in your source folder, it’ll be searching in your compiled files.

In this case, sample.fxml should be directly in the root folder of our compiled directory – in IntelliJ, that’s PATH_TO_PROJECT/target/classes.

  1. If it doesn’t start with “/”, resolveName() uses the package name of the class that’s doing the loading and then appends the filename provided.

In this case, we called getClass().getResource() in Main, so the class is Main and the package is com.edencoding.

Starting at the root directory (PATH_TO_PROJECT/target/classes), the resource should therefore be located at:

(PATH_TO_PROJECT/target/classes/)com/edencoding/sample.fxml.

What that means before compiling

In our case, let’s see the different ways we could load the sample.fxml file and where we’d need to store it in our project.

  1. We use the location “/sample.fxml: the getResource() method will look directly in the resources folder.
  2. Instead, we use “sample.fxml: it will look for resources/com/edencoding/sample.fxml

2. Determining the correct path to a resource

With those rules set, we can begin to organise our resources more gracefully. In fact, in larger projects it’s essential to organise your resources. The two most common ways to do this are:

  1. By resource type
  2. By package

Or both..

a. Setting location by type

Organising resources by type is perhaps the most common. To do this, you have to modify the string you pass into the getResource() method.

It will still navigate to either the root, or package, depending on whether you’ve started your string with "/".

As an example, one of the first things I always do is add a logo. It helps make it look slightly more professional, and conforms with user’s expectations.

Here, I’ve separated my resources into to files.

To find the right string to pass getResource() we see:

  1. The img and fxml directories are in the root of the project, so we’ll start the resource name with "/".
  2. I want resolveName() to navigate through the image directory "img" to my resource EdenCodingIcon.png. To achieve this, I set the remaining string to "img/EdenCodingIcon.png"

So, the full string is:

/img/EdenCodingIcon.png

b. Setting location by package

Organising by package is also useful for separating different views based on their function. For example, we might have another package called auth that is responsible for signing in users.

In this case, the correct way to locate the resource must be using a class inside the auth package. If you are not loading it from inside the package, you can use the static class attribute rather than the getClass() method.u

URL fxmlLocation = LoginController.class.getResource("login.fxml");
FXMLLoader loader = new FXMLLoader(fxmlLocation);

To find the right string to pass getResource() we see:

  1. The login.fxml file is in package "auth", so we’ll start the resource name without a leading "/". This will automatically start the search in “auth” because LoginController is in that package.
  2. I want resolveName() to find my resource in the package root. To achieve this, I set the remaining string to "login.fxml"

So, the full string is:

login.fxml

c. Setting location by package and type

Finally, the most complicated this gets is storing resources both by package and by folder. In this case, let’s extend the example above and store the login.fxml file inside a directory to separate it from the other images I need in the login View.

I’ve collapsed the java folder, but it has the same basic structure as before. For one last time, we find the right string to pass getResource() by:

  1. The login.fxml file is in package "auth", so we’ll start the resource name without a leading "/". This will automatically start the search in “auth” because LoginController is in that package.
  2. I want resolveName() to navigate through the fxml directory "fxml" to my resource login.fxml. To achieve this, I set the remaining string to "img/login.fxml"

So, the full string is:

img/login.fxml

3. Directory errors

The last thing that can go wrong has nothing to do with Java, or JavaFX – and everything to do with how IDE’s try to be helpful.

IntelliJ tries to make your life easier, it really does. It’s used to people defining reverse-DNS packages like com.edencoding. So, if you right-click on a source file you’re directed to create a package. This does not happen with resources!

Source Files: If you add a compound package name like com.edencoding, it will create multiple directories with simple names (e.g. com with a directory edencoding inside it).

Resource Files: If you add a compound directory name to a resource, it will create a single directory with a compound name (e.g. one directory called com.edencoding)

Then, during the build process, your resources are moved according to the same rules as always – directory structure. You can see here, in the top example, we made sure that our structure was com/edencoding/ by creating directories properly.

In the second, we created a single directory called “com.edencoding” in the resources directory. Our IDE, which doesn’t know it’s doing anything wrong, moves sample.fxml to the wrong place.

The lesson here is not to shortcut the directory-creation process for the resources folder. It doesn’t behave the same way as the java source folders!

Thankfully, there’s an easy fix.

Fix: Manually navigate into source folder structure and fix the compound directory. Remember to do this in your source folder not your target folder otherwise your good work will be overridden on the next build!

Conclusions

The “Location is not set” error is thrown when the FXMLLoader runs the load() method with a null resource. This is most commonly generated by passing an incorrect resource location to getClass().getResource().

To debug this code, instantiate a standalone URL object, and check whether it is null. If it is, follow the resolveName() algorithm to determine the correct path to your resource.

Occasionally, this error is generated by directories with a compound name (directories called something.somethingElse) These are hidden by the IDE, because it automatically collapses packages to look like this anyway. To fix this, navigate to the source folder in the file structure, and correct the directory structure manually.