If I had a quarter for every time I have seen this damn dagger error…

Caused by: java.lang.IllegalArgumentException: No inject registered for {path.to.SomeClass}. 
You must explicitly add it to the 'injects' option in one of your modules.

Let’s be honest: this error is the hazing of Android development with Dagger*. My colleagues and I have spent oodles of time trying to figure out why we are getting this.

We are not alone, if the litany of StackOverflow questions and other internet hits for this error is any indication.

*Clarification: this article is specific to Dagger 1.2, the version of Dagger written and maintained by Square. Dagger 2, maintained by Google, is not a later version of 1.2—it is a completely different version by a different team, and it works fairly differently to Dagger 1.2.

This post is an attempt to lead you, step by step, out of the clutches of this infuriating error. Given the number of reasons that you could be getting this error, the first few drafts will probably be incomplete. If this post does not help you resolve the error and you figure out how to resolve it by some other means, please e-mail me (chelsea at chelseatroy.com ) and let me know how you did solve the issue, so I can update this post to help the next person who ends up in your permutation of this problem.

Sometimes when you get this error, the problem really is that you need to add your injected class to the ‘injects’ option in one of your modules. The more annoying situation, and the one that probably brought you here, is the case where you have already registered your injects, like so:

@Module(
        includes = {
                ProjectsModule.class,
        },
        injects = {
                YourClassThatYouWantToInject.class,
        },
        complete = false,
        library = true
)
public class AppModule {

In cases where you are already registering the class you’re being asked to register, the error is non-descriptive and even misleading.

But before you write off the Square engineers who wrote dagger, you should know something:

It’s not dagger’s fault. 

Dagger can’t know what went wrong because this is a runtime error: something went off the rails when you tried to inject dependencies while running your app. This could happen because of a dagger problem, but it could also happen because of a problem with the way your app is creating the classes upon which your dagger setup depends.

The sometimes-misleading error message looks to me like an honest attempt on the part of the dagger team to give a more helpful error message than “Oops, something went wrong.” It’s just not always relevant, unfortunately.

So, to diagnose your problem, we’re going to talk about how Dagger works with your app to stick your dependencies where you need them. Then we’ll talk about how to debug that process, step by step.

How Stuff Works: From Clicking the App Icon to Dependency Injection

When you (or your editor) launches your Android app, Android begins in the Manifest file. Your AndroidManifest.xml lives at the root of your app, and it looks something like this:

Apologies for screenshot: WordPress does not love displaying XML, evidently.
Apologies for screenshot: WordPress does not love displaying XML, evidently.  Here is the repo where I keep the app used in all the examples in this post. 

Your Manifest file tells OS critical information about your app.  The manifest file above belongs to an application that uses dagger for dependency injection.

Inside the ‘application’ block, you see an attribute called ‘name.’ If this attribute is present, then Android looks for a class with this name to start your app. If it doesn’t find one, then it uses Android’s own Application class. In the case of this app, ZooniverseApplication.class is a class in my app that extends Android’s Application class. In its onCreate() method, I set up my dependency injection:

    @Override
    public void onCreate() {
        super.onCreate();

        ObjectGraph graph = ObjectGraph.create();
        graph.plus(new AppModule(this));

        //^ not done this way in the actual repo, but I wrote
        //the simplest thing you could do here to avoid confusing the        
        //point of this blog post. For more info on the way it's done        
        //in the real repo, see this post. 
    }

    public void inject(Object object) {
        graph.inject(object);
    }

So I need to have that name in my manifest file, or this onCreate() method will never get called. On two occasions where I’ve seen the dreaded dagger error, this was the exact problem.

Of course, the onCreate() method of a subclass of application is not your only option for initiating dependency injection. You could, in theory, do it in the onCreate() method of your launch activity, for example. In any case, if you’re getting the error and you don’t know why, step one is to make sure you are creating your object graph and adding your module (or modules). If the module is not added that contains your class in its ‘injects’ block, then dagger cannot see it or use it.

To troubleshoot: try throwing a new RuntimeException on the line right before the line where you create your object graph. Rub the app: if the exception gets thrown, you probably are creating the object graph. Now take out that RuntimeException and throw another one on the line right before the line where you add your module (or the module that contains the injects statement that the dagger error is complaining about not having). Again, run the app. If the exception gets thrown, the you probably are adding the module in question. If, on either occasion, the exception does not get thrown, then you have found a plausible cause for the error. The next step is to figure out why those lines are not getting called.*

*Alternatively, you can also set a breakpoint on the line in question itself.

Now, let’s talk about Dagger itself.

In the onCreate() method that you see above, I create my object graph and add my top-level module to the object graph. Let’s go into the module and see what happens in there.

@Module(
        includes = {
                ProjectsModule.class,
        },
        injects = {
        },
        complete = false,
        library = true
)
public class AppModule {
    private final ZooniverseApplication application;

    public AppModule(ZooniverseApplication application) {
        this.application = application;
    }

    @Provides
    @Singleton
    ZooniverseApplication application() {
        return this.application;
    }

    @Provides
    @Singleton
    Context applicationContext() {
        return this.application.getApplicationContext();
    }

This module does not inject any classes on its own. Instead, it includes a submodule called ProjectsModule. Classes injected by ProjectsModule have access to anything provided by the ProjectsModule as well as anything provided by the ApplicationModule shown here. This is the ProjectsModule:

@Module(
        injects = {
                ProjectListActivity.class,
                ProjectListFragment.class,
                ProjectsRequestGenerator.class,
        },
        complete = false,
        library = true
)
public class ProjectsModule {

    public ProjectsModule() {
    }

    @Provides
    AppBroadcastReceiver appBroadcastReceiver() {
        return new AppBroadcastReceiver();
    }

    @Provides
    LinearLayoutManager linearLayoutManager(Context context) {
        return new LinearLayoutManager(context);
    }

    @Provides
    ProjectListAdapter projectListAdapter() {
        return new ProjectListAdapter();
    }
}

When the object graph is created, Dagger assigns objects to their dependencies like this:

Diagram of Dagger

To see an example of how these modules work, let’s take a look at one of the classes in the app and trace how it gets all its dependencies.

public class ProjectListFragment extends Fragment implements BroadcastResponder{
    @Inject
    AppBroadcastReceiver appBroadcastReceiver;

    @Inject
    LinearLayoutManager layoutManager;

    private ArrayList projectList = new ArrayList();

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getActivity().getApplication().inject(this);
    }

So this thing has two dependencies: the AppBroadcastReceiver and the LinearLayoutManager.

To get the AppBroadcastReceiver, dagger looks into the Module where the ProjectListFragment is registered. There it finds a @Provides for an AppBroadcastReceiver, so it gets the AppBroadcastReceiver from there.

To get the LinearLayoutManager,  dagger looks into the Module where the ProjectListFragment is registered. There it finds a @Provides for an LinearLayoutManager, but that LinearLayoutManager needs a context. So, Dagger looks throughout the ProjectsModule for a provided context, but there isn’t one.

So, Dagger looks to see if ProjectsModule has an umbrella module (a module that includes the ProjectsModule) that provides a context. It does! The AppModule has a @Provides for a context. So it gets a context from there, gives it to the LinearLayoutManager, and provides the LinearLayoutManager to the ProjectListFragment.

If something in this flow is out of order, then Dagger may have trouble figuring out where to get the things that your class is asking for.

Imagine, for example, that the ProjectListFragment were registered in the AppModule instead of the ProjectsModule. When Dagger cannot find a dependency provided in the module where your class is registered, it starts looking up the module tree, not down. So it’s not going to find the LinearLayoutManager or the AppBroadcastReceiver. Even though you have them in an inject block somewhere, it’s not somewhere that Dagger can see as far as the ProjectListFragment is concerned. So, as far as Dagger knows, you still need to add your class to a relevant ‘inject’ block.

to troubleshoot: Remember that Dagger falls back on unlisted dependencies by going up the module tree, not down. If a dependency is registered in the ‘inject’ block and your Dagger setup is giving you the error that says it’s not, look for any dependencies it has that appear in modules below the problem module.

I also saw this happen once in a situation where a class got registered twice in the same object graph. A class got registered in the ‘inject’ blocks of two different modules—let’s call them A and B. B provided a dependency that depended on something in A, but in the main AppModule where both of them got included, Module B appeared ahead of Module A in the list of modules that the main AppModule included. So the class went to get the dependency it needed from Module B first, and the dependency’s dependency on the thing in Module A was not yet available. This took some time to figure out, so I would recommend registering each of your classes in only one ‘inject’ block in your Dagger setup, for the sake of simplicity. Once something is needed by more than one module, move it into a higher module rather than registering a class in multiple modules.

to troubleshoot: An ounce of prevention is worth a pound of cure. Pull out those classes that get listed in the ‘injects’ blocks of multiple modules, and arrange your tree so that Dagger only has one road to the main module from each injected class.

Dagger has some other errors related to the @Module annotation that can take a while to figure out—more on them in a later post.

 

 

 

 

Advertisements

2 thoughts on “If I had a quarter for every time I have seen this damn dagger error…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s