55

I'm having a hard time trying to pinpoint which, of the very many, methods I have in my angular app that would be causing the error:

Uncaught Error: [$injector:unpr] Unknown provider: nProvider <- n

This only happens once the javascript has been bundled & minified by ASP.Net.

I have ensured that all the controllers, and any other DI, is using the minification-safe method, I.E My controllers/service etc are using the method:

appControllers.controller('myCtrl', ['$scope', function($scope){
        //......
}]);

I've gone through every JS file in our app - there are a lot... and can't find anything that violates this way of injecting dependencies - though there must be one somewhere...

Is there a better way to pinpoint which method could be causing this error?

Thanks

Darren Wainwright
  • 30,247
  • 21
  • 76
  • 127
  • My guess is the minifier is changing the parameter names of your functions. Since DI items are registered through a string (not modified by minification) they will not match anymore an DI fails. – Marvin Smit May 05 '14 at 19:55
  • yes, that would be the case, though all (that i can see) of my DI is done using the safe way. Just can't seem to find the one that isn't... – Darren Wainwright May 05 '14 at 19:56
  • Although not a solution; you could include the non minified version of angular + minified custom code. That would allow you to set a breakpoint on the reported error and backtrace it somewhat (by logical comparison of weird named functions to your own) from there. – Marvin Smit May 05 '14 at 20:03
  • @MarvinSmit - that's what I'm doing at the moment - I beutified my minified JS and am trying to trace through - almost impossible (so far anyway) – Darren Wainwright May 05 '14 at 20:07
  • Been struggling through this too. We are positive that none of our code has a problem, yet we still end up with this same thing under minification. I found a post somewhere that said there was something in angular itself that was causing it - but I cannot find it now, nor have I been able to prove this to myself. Ended up giving up and just not doing minification - for now. – aet May 05 '14 at 20:25
  • 2
    I found our problem.. We have a 3rd party directive - https://github.com/durated/angular-scroll - NONE of the DI methods were written to be safe for minification. Adjusted them and it's all good now. – Darren Wainwright May 05 '14 at 20:29
  • What I did to find it was beutify my minified JS - just because it's a lot easier to read. And then used the developer toolkit on IE (and Chrome) to view the call stack. helped back trace to find possible problems. – Darren Wainwright May 05 '14 at 20:30
  • If possible add this as an answer & accept it, so that others benefit from it. – Nilesh May 05 '14 at 22:40

9 Answers9

105

For anyone else struggling with this problem, I found an easier solution. If you pull open your developer console (on chrome) and add a breakpoint where angular throws the error:

angular throwing an error

Then, on the stack trace on the right, click on the first "invoke" you see. This will take you to the invoke function, where the first parameter is the function angular is trying to inject:

I was able to inspect the function

I then did a search through my code for a function that looked like that one (in this case grep "\.onload" -R public), and found 8 places to check.

starball
  • 20,030
  • 7
  • 43
  • 238
yourdeveloperfriend
  • 1,600
  • 1
  • 9
  • 19
  • 6
    This should be the answer! Saved me hours of scouring through the codebase, which would have been futile, considering where we made the mistakes. Many thanks! – Angel Yordanov Oct 28 '14 at 17:33
  • 4
    Your example adds breakpoints in un-minified javascript. OP is having trouble with minified js. How would this solve the problem if the error only occurs after minification? – 111 May 12 '15 at 20:29
  • 1
    @glyph I just changed my build process so angular.js wasn't minified and all others still are, to be able to do this. – mikeStone Jun 18 '15 at 22:03
  • 3
    This should be the accepted answer. And it gets even better: When you hover over the `fn`, and see the popup with the function, you can click on the function and you'll be directly taken to the function in question. (Don't forget to use Chome's auto formatting of your minified code to get a better overview!) – Jasper Schulte Dec 06 '15 at 14:25
  • To use this strategy with minified angular.js, search for $get in the call stack, that will take you were the invoke method is called. – rixo Dec 08 '15 at 12:15
  • Outstanding answer. I learned at one fell swoop how to dive into Angular's rather frustrating stack traces, that controllers in directives need the minification-safe syntax, and that Chrome dev tools can unminify code! Wonderful. – Rich Churcher Apr 13 '16 at 02:29
  • In newer versions of chrome (I'm on V50) you can print the function to the console (in this case just type `fn`) and then click the output. That will take you to the exact function definition which will probably be (relatively) easy to identify. – David says Reinstate Monica Apr 27 '16 at 18:29
  • Actually, you can use this great trick without changing your build process: you just have to throw the error in non-minified mode, which can be done with the solution given by @MaxSchulze. – PhiLho Jul 26 '16 at 11:59
  • Agree. This should be the accepted answer. Note - in latest version of AngularJs the code to look for is now: return new ErrorConstructor(message); – Matt Jan 20 '17 at 13:15
  • Thanks a lot man I am able to find the issue in my application. – Arpan.exe Dec 08 '17 at 12:23
55

For anybody reading this, using Angular 1.3

You can now use Angular's ng-strict-di check like this:

<div ng-app="some-angular-app" ng-strict-di>
  ...
</div>

This will give you an appropriate error message if you didn't load your dependencies using the array syntax.

Max Schulze
  • 1,173
  • 7
  • 8
  • do you know you to do this if you manually bootstrap the app instead of using ng-app in html? – Neil Feb 13 '15 at 11:01
  • Hm not sure, maybe check the document of ngStrictDi, maybe you can inject it somehow and load it? – Max Schulze Feb 14 '15 at 12:37
  • 5
    @Neil You can pass it in the options argument in the bootstrap function `{ strictDi: true }` as described [here](https://docs.angularjs.org/guide/di) under the heading _Using Strict Dependency Injection_ – Joel Mitchell Mar 06 '15 at 14:56
  • This should be the correct answer to this question. It works very well and saved me a lot of time eyeballing the code. – Richard G May 31 '16 at 02:03
  • 2
    https://docs.angularjs.org/error/$injector/strictdi?p0=controller The error message is less explicit that I expected: it doesn't tell which controller is faulty. :-/ Basically, it only confirms we will have trouble in minified mode... – PhiLho Jul 26 '16 at 11:51
  • Ah, but actually, it works great when combined with @yourdeveloperfriend solution! This throws an error in non-minified mode, so you can easily trace it as described... – PhiLho Jul 26 '16 at 11:58
  • Honestly - this is the best answer for me. Solved all my issues that I have been struggling for hours in minutes. I didn't even have to fight the minified battle! Thanks! – anbiniyar Sep 02 '16 at 08:46
  • @PhiLho you have to use an unminified version of angular to get helpful error messages. I recommend just linking to an unminified version via CDN while debugging this. – user3413723 Oct 29 '16 at 04:24
  • I wish I would of know about ng-strict-di months ago. Thanks. This really should be the default as without it, it's hours of debugging waiting to happen. – Si-N Mar 15 '17 at 10:38
  • This should be the answer. Plus if you bootstrap your app you can use. angular.bootstrap(document.body, ['app'], { strictDi: true }); to inject strictDi mode. – Mark Odey Nov 08 '18 at 23:17
8

I had the same problem and I found a solution that could be helpful for the rest. What I propose is basically what I saw in the comments and docs. If you are using Angular 1.3.0 or above, you can use this:

<html ng-app="myApp" ng-strict-di>
   <body>
      I can add: {{ 1 + 2 }}.
      <script src="angular.js"></script>
    </body>
</html>

In my case, I have everything within a file app.js so the only thing I need to do for finding my DI problems is to add this little code at the end:

angular.bootstrap(document, ['myApp'], {
  strictDi: true
});

It's better documented in Angular Docs

I hope that helps. Good luck!

Jesús Sobrino
  • 387
  • 2
  • 5
4

As mentioned in the comments, These are the steps I took to try and find my JS error.

If there is another, easier, solution, please feel free to post it and I may mark it as accepted.


Trying to debug minified code is a nightmare.

What I eventually did was copy my minified javascript, directly from the inspector in Chrome.

I then pasted the JS into http://www.jspretty.com/ - I had tried http://jsbeautifier.org/ but found their site froze with such large JS code.

Once it was 'pretty-fied' I created a test.js file in my solution and pasted the, now easier to read code, into it.

Quick step to comment out the @script tag in my _layout and add a link to the test.js file and I was ready to debug a now, far easier to read, chunk of Javascript.

It is still pretty awkward to traverse the call stack, though now you can see actual methods it makes it far less impossible.

Darren Wainwright
  • 30,247
  • 21
  • 76
  • 127
3

Something that helped me solve this (finally!) was actually already in the angular docs! If you add the ng-strict-di attribute to your code wherever you define your ng-app, angular will throw a strict warning so you can more easily see what's going on in development mode. I wish that was the default!

See the argument list at the bottom of the ngApp docs.

https://docs.angularjs.org/api/ng/directive/ngApp

0

The way this works for me is the following:

1) have two test specification html files (unit test) minimized and plain

2) make sure the bundling of the files are in the same order as the plain spec file (JS script reference)

3) make sure to explicitly declare all dependencies (array or $inject declares see http://www.ozkary.com/2015/11/angularjs-minimized-file-unknown-provider.html)

When there is a mistake on the unit test (miminized reference) file, I can compare and make sure the reference of the files is in the correct order as the working file.

hope that help.

ozkary
  • 2,436
  • 1
  • 21
  • 20
0

I had the similar issue and used lots of time to investigate and figured out it was the Chrome extension Batarang that was injecting the bad code and error in Angular looked exactly the same. It's really a pity it's so hard to find what exactly is causing the problem.

Ilya Chernomordik
  • 27,817
  • 27
  • 121
  • 207
0

I had the similiar issue too. The solution is exacly the answer from ozkary point 3, that is to make sure to explicitly declare all dependencies including "resolve" part of your route.

Below is my code.

when('/contact/:id', {
      controller: 'contactCtrl',
      templateUrl: 'assets/partials/contact.html',
      resolve: {
         contact: ['ContactService', '$route', function(ContactService, $route) {
            return ContactService.getContactDetail($route.current.params.id);
         }]
      }
})
Ajang R
  • 49
  • 1
0

For those who's bootstrapping their angularjs app.

angular.bootstrap(body, ['app'], { strictDi: true });

Don't forget to debug in non minified code and you should be able to figure out pretty quickly the malformed dependency injection.

The malformed injection is usually formatted like this :

...
.run([ function(ServiceInjected){
...

But should look more like this

...
.run(['ServiceInjected', function(ServiceInjected){
...   

This is tested in angularjs 1.7.2

Mark Odey
  • 170
  • 2
  • 12