40

I'm getting the following error:

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

I know this is being caused by the minification process and I understand why. However is there an easy way to determine which file is actually causing the issue?

Brett Postin
  • 11,215
  • 10
  • 60
  • 95
  • 1
    minification issue most likely. Looks like your provider has been renamed to `n`. – Davin Tryon Mar 05 '14 at 12:08
  • @DavinTryon As my question states I'm already aware of the cause. I'm looking for a way to easily identify the problem file without having to manually search through hundreds of files. – Brett Postin Mar 05 '14 at 12:18
  • Usually the error will have a line number in the minified file, you could look there and figure out which resource is causing the issue. – Davin Tryon Mar 05 '14 at 12:21
  • @DavinTryon traversing the call stack in the console just takes me through Angular code. – Brett Postin Mar 05 '14 at 12:25
  • 1
    +1 @DavinTryon. The stack trace only shows Angular itself choking, not which file is causing the issue. Have you by chance figured it out yet? – Kim Miller Mar 24 '14 at 20:34
  • @KimMiller No sorry. The best I could do was step through the minified code. Not ideal for something that could very easily be introduced. – Brett Postin Mar 25 '14 at 08:57
  • @Brett Postin - Usually the information given (via line numbers, etc) in the error message will help you narrow it down to a specific module, factory, directive, controller, etc. So you can look only at what is injected to that item. Then you can apply the answer from package on a most-likely to least-likely basis. I just had this issue with a jasmine test that would run fine in the browser and headless but would die on our Jenkins server where it was minified. I used his #2 solution and applied it to the related directive controller and it solved my problem. – KSev May 20 '14 at 21:01
  • The best solution is [this](https://stackoverflow.com/a/21787020/3767429) great answer! – roy650 Feb 13 '19 at 14:31

4 Answers4

28

Angular 1.3.x has an ng-strict-di directive that is placed on the same element as the ng-app directive. This element causes your app to throw an error whenever dependencies have not been annotated. While it still doesn't give you the line number of the offending code, it does give you the function with its parameters (i.e. function($scope, myServiceName)) which is hopefully unique enough that you can find it pretty quickly in a good code edit.

A good overview of the directive: ng-strict-di.

willlma
  • 7,353
  • 2
  • 30
  • 45
ansorensen
  • 1,276
  • 1
  • 14
  • 29
  • 1
    Outstanding! This worked for me after having spent hours trying to find the issue using alternative means. Turns out my typescript static $inject was missing the '$'. – spoof3r Nov 19 '15 at 14:46
  • I'm sorry I don't understand how did you used it ? I'm having the same problem and it has been hours since I've started investigating why this happend ! – H.Elsayed Jan 25 '17 at 10:20
7

I understand the question and I have an answer, it's only slightly convoluted.

The way I found the problem was to rename all identifiers to make them ALL unique, then you get something useful to look for in your compiled javascript which will hopefully point you towards the culprit.

download my modified version of uglify (pull request pending...)

brew install node if you don't have node installed.

./bin/uglifyjs --unique_ids original.min.js >new.min.js

Now replace your compiled js with new.min.js and load your app to reproduce the problem now you should get a dependency injection error like n4536

If your editor is awesome with super long lines you can just load new.min.js, look for n4536 and hopefully that'll help you identify the culprit.

If not this'll work to print some context around the problem. egrep -o '.{199}n4536.{99}' new.min.js

ark
  • 148
  • 1
  • 8
  • 2
    This solution works when you apply [the fix](https://github.com/arkarkark/UglifyJS/pull/1) – Oleksandr.Bezhan Oct 04 '14 at 10:03
  • Thanks Oleksandr! I guess I forgot a commit/push somewhere. – ark Oct 07 '14 at 23:43
  • Couldn't you just "rename" every parameter to the same thing plus some unique bit? E.g., 'myProvider' becomes 'myProvider_12345' instead of 'e'. Would make it much easier to track the problem down. – MindJuice Dec 24 '14 at 19:55
  • @MindJuice that's exactly what my change to uglify does. – ark Dec 26 '14 at 11:19
  • Perhaps I misunderstood, but it looks to me like your new name does not incorporate anything from the original name, just a letter and a number, but I haven't actually tried your change yet. – MindJuice Dec 31 '14 at 23:58
  • Ah, I got ya, I'm dealing with minified code, so the myProvider part is all minified down to 'e' which is the whole problem. If you have the myProvider part, you can likely find it in your code very easily. – ark Jan 02 '15 at 02:25
6

Angular's injector has 3 ways to resolve dependencies for you:

1. Inferring dependencies from function argument names. This is most used in all angular's examples, e.g.

app.controller('MyController', function($scope, MyService) { ... });

In this case injector casts function as string, parses argument names and looks for services/factories/anything-else matching that name.

2. Inline annotations. You might also encounter this syntax:

app.controller('MyController', ['$scope', 'MyService', function($scope, MyService) { ... }]);

In this case you make it much easier for the injector, since you explicitly state names of dependencies you require. The names are enclosed in quotes and js minifiers do not modify strings in code.

3. Inline annotations as property. If you define your controllers as functions, you might set annotations in special property $inject:

function MyController($scope, MyService) {...}
MyController.$inject = ['$scope', 'MyService'];

In this case we also explicitly state dependencies.

My guess is you're using the solution no. 1. Once minifier changes names of your implicitly defined dependencies, injector no longer knows, what are your function's dependencies. To overcome this you should use 2nd or 3rd way of annotating dependencies.

package
  • 4,741
  • 1
  • 25
  • 35
3

While there does not appear to be any great way to debug these DI issues if you have no idea where to look, I had a sense mine was in a less than obvious place ... and it was:

App.Services = angular.module('spokenvote.services', ['ngResource', 'ngCookies'])
    .config(servicesConfig)
    .run(($rootScope, $location) -> $rootScope.location = $location)

needed to be:

App.Services = angular.module('spokenvote.services', ['ngResource', 'ngCookies'])
    .config(servicesConfig)
    .run(['$rootScope', '$location', ($rootScope, $location) -> $rootScope.location = $location])
Michael0x2a
  • 58,192
  • 30
  • 175
  • 224
Kim Miller
  • 886
  • 8
  • 11
  • and mine `$httpProvider.interceptors.push(['$q',function($q) {` , waisted 5 hours of my time because I was only looking at controller, directives and filters – mohas May 19 '18 at 18:58