diff --git a/docs/content/error/ngRepeat/badident.ngdoc b/docs/content/error/ngRepeat/badident.ngdoc
new file mode 100644
index 000000000000..9f095cf57be4
--- /dev/null
+++ b/docs/content/error/ngRepeat/badident.ngdoc
@@ -0,0 +1,39 @@
+@ngdoc error
+@name ngRepeat:badident
+@fullName Invalid identifier expression
+@description
+
+Occurs when an invalid identifier is specified in an {@link ng.directive:ngRepeat ngRepeat} expression.
+
+The {@link ng.directive:ngRepeat ngRepeat} directive's `alias as` syntax is used to assign an alias for the processed collection in scope.
+
+If the expression is not a simple identifier (such that you could declare it with `var {name}`, or if the expression is a reserved name,
+this error is thrown.
+
+Reserved names include:
+
+ - `null`
+ - `this`
+ - `undefined`
+ - `$parent`
+ - `$even`
+ - `$odd`
+ - `$first`
+ - `$last`
+ - `$middle`
+
+Invalid expressions might look like this:
+
+```html
+
{{item}}
+{{item}}
+{{item}}
+{{item}}
+```
+
+Valid expressions might look like this:
+
+```html
+{{item}}
+{{item}}
+```
diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js
index 1d7d516fe8c9..95effc6d33fc 100644
--- a/src/ng/directive/ngRepeat.js
+++ b/src/ng/directive/ngRepeat.js
@@ -266,6 +266,12 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var valueIdentifier = match[3] || match[1];
var keyIdentifier = match[2];
+ if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
+ /^null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent$/.test(aliasAs))) {
+ throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
+ aliasAs);
+ }
+
var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
var hashFnLocals = {$id: hashKey};
diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js
index aeaeba3849a5..d2d81a6af87f 100644
--- a/test/ng/directive/ngRepeatSpec.js
+++ b/test/ng/directive/ngRepeatSpec.js
@@ -416,6 +416,52 @@ describe('ngRepeat', function() {
expect(trim(element.text())).toEqual('No results found...');
});
+
+
+ it('should throw if alias identifier is not a simple identifier', inject(function($exceptionHandler) {
+ scope.x = 'bl';
+ scope.items = [
+ { name : 'red' },
+ { name : 'blue' },
+ { name : 'green' },
+ { name : 'black' },
+ { name : 'orange' },
+ { name : 'blonde' }
+ ];
+
+ forEach([
+ 'null',
+ 'this',
+ 'undefined',
+ '$parent',
+ '$index',
+ '$first',
+ '$middle',
+ '$last',
+ '$even',
+ '$odd',
+ 'obj[key]',
+ 'obj["key"]',
+ 'obj[\'key\']',
+ 'obj.property',
+ 'foo=6'
+ ], function(expr) {
+ var expression = ('item in items | filter:x as ' + expr + ' track by $index').replace(/"/g, '"');
+ element = $compile(
+ '')(scope);
+
+ var expected = new RegExp('^\\[ngRepeat:badident\\] alias \'' + escape(expr) + '\' is invalid --- must be a valid JS identifier which is not a reserved name');
+ expect($exceptionHandler.errors.shift()[0].message).
+ toMatch(expected);
+ dealoc(element);
+ });
+
+ function escape(text) {
+ return text.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
+ }
+ }));
});