skip to content

A production Angular app's error tracker shows only 'Error: NG0201' with no message; how do you read Angular's NG error codes and debug this?

level: seniorimportance: should knowfreq 36%

answer

  1. the code is the stable part
  2. messages are stripped when optimized
  3. angular.dev/errors plus the code
  4. reproduce with a development build

basics

~20 s

Angular errors carry a stable NG code; production builds strip the descriptive text, leaving only the code. Look it up at angular.dev/errors/NG0201 (no provider found), then reproduce in a development build, where the full message names the missing token and links to that page.

solid answer

~50 s

Every Angular error has an **NG code**. Runtime errors are formatted as `NG0` plus a number (`NG0201`, `NG0203`, `NG0950`); compile-time errors from the compiler use `NG1xxx`-`NG8xxx`. In development the message is descriptive - for `NG0201` it names the missing token, the injector it was requested from and the dependency path - and for documented codes it ends with `Find more at https://angular.dev/errors/NG0201`. In an optimized build the text is passed as `false` and tree-shaken, so only the code survives. So: look the code up on the error reference; `NG0201` means a token was injected with no provider on the resolution path. Then reproduce with a development build (or a deployment built with `optimization: false`) to see which token, which injector and which chain of dependencies. Typical causes: a service without `providedIn: 'root'` and missing from any `providers`, a provider scoped to a lazy route or component the failing code is outside of, or a feature provider function never added to `bootstrapApplication`.

code

ts · 12 lines
ts
import { Injectable, inject, Component } from '@angular/core';

@Injectable() // no providedIn, and not listed in any providers array
export class CartService {}

@Component({ selector: 'app-cart', template: '' })
export class Cart {
  // Development: an NG0201 message naming CartService, its source injector and dependency path,
  //   ending with "Find more at https://angular.dev/errors/NG0201"
  // Optimized build: the text is stripped and only "NG0201" remains
  private cart = inject(CartService);
}

go deeper

for a junior

Know that Angular errors have NG codes and that angular.dev/errors plus the code explains each one.

for a middle

Explain why production shows only the code, and name the usual causes of NG0201 and NG0203.

for a senior

Run the full routine on a bare production code: reference page, source-mapped stack, dev reproduction, injector inspection, fix and regression test.

for a principal

Set up the observability that makes terse production errors actionable: codes as searchable fields, private source maps and a diagnostic environment.

## Anatomy of an Angular error code Angular throws its own errors through a `RuntimeError` class that always carries a numeric **code**. The code is formatted with an `NG` prefix: | Range | Where it comes from | Examples | | :-- | :-- | :-- | | `NG0xxx` and longer `NG0...` codes | Runtime errors thrown in the browser or on the server | `NG0201` no provider, `NG0203` `inject()` outside an injection context, `NG0950` required input read too early | | `NG1xxx`-`NG8xxx` | Compile-time diagnostics from the Angular compiler | `NG1001` decorator argument not a literal, `NG8001` unknown element | The leading `0` on runtime codes distinguishes them from compiler diagnostics. The numbers are stable across releases, which is what makes them useful in logs and searches. ## Why production shows only the code In source, Angular writes errors like `new RuntimeError(code, ngDevMode && 'No provider found for ...')`. In development `ngDevMode` is truthy, so the message is included - for a missing provider it names the token and adds `Source:` and `Path:` details showing the injector and the chain of dependencies that led to it. For codes that have a reference page, the framework appends `Find more at https://angular.dev/errors/NGxxxx`. In an optimized build the CLI defines `ngDevMode` as `false`. The message expression becomes `false`, the string is tree-shaken out, and for a missing provider the injector throws a fresh error with no text at all, so the message is just the code: `NG0201`. This keeps bundles small, at the cost of terse production logs. ## A debugging routine for a bare code 1. **Look it up.** Open `https://angular.dev/errors/NG0201`. Every documented code has a page explaining causes and fixes. 2. **Locate it.** Use source maps in your error tracker, if they are uploaded, to find the component or service in the stack trace. 3. **Reproduce in development.** Run the same flow with `ng serve`, or deploy a diagnostic build with `"optimization": false`, to get the full message - here, which token had no provider. 4. **Confirm with tooling.** Angular DevTools' Injector Tree shows the resolution path and each injector's providers; in the console, `ng.getInjector(el)` reaches the injector at an element. 5. **Fix the cause, not the symptom**, then add a test that exercises the path. ## `NG0201` in practice `NG0201` means dependency injection walked from the requesting injector up to the root, and on to the null injector, without finding a provider for the token. Common causes: - a service class with `@Injectable()` but **no `providedIn: 'root'`** and not listed in any `providers` array; - a provider registered on a **lazy route** or a **component**, while the failing consumer lives outside that subtree; - a feature's provider function (for example `provideHttpClient()`) never added to the application's providers in `bootstrapApplication`; - in tests, a `TestBed` configuration that omits a provider the real app registers. ## Other codes worth recognising - **`NG0203`**: `inject()` called outside an injection context - for example in a `setTimeout` callback or a method called after construction. Move the call to a field initializer or constructor, or wrap it in `runInInjectionContext`. - **`NG0950`**: a required input was read before a value was bound, typically during construction. Read it in `ngOnInit` or later, or inside a reactive context such as `computed()`. - **`NG0200`**: a circular dependency in DI. ## Reading the stack when there is no message Even without text, the stack trace carries information. With source maps applied, the top frames usually show the factory of the class whose dependency failed, for example `Cart_Factory`, followed by the view creation that instantiated it. That points at the *consumer*; the missing *provider* is then a question of where the token should have been registered for that consumer's injector path. ## What to put in place before the next incident - Upload **source maps** to your error tracker (without serving them publicly) so stack traces point at real files. - Keep a **non-optimized diagnostic environment** for reproducing issues with full messages and DevTools. - Log the **code** as a searchable field; it is the only part guaranteed to survive optimization.

  • Why do Angular runtime error codes start with NG0 while some start with NG8?
    The `0` marks a runtime error thrown by the framework while the app runs; `NG1xxx` to `NG8xxx` codes are compile-time diagnostics from the Angular compiler, such as `NG8001` for an unknown element. You meet the first kind in logs and the browser, the second kind in `ng build` output and the editor.
  • How does Angular keep full error messages in development but not ship them in production?
    Messages are written as `ngDevMode && 'text'`. Optimized CLI builds define `ngDevMode` as `false`, so each message expression becomes `false` and the string is removed by tree-shaking. Only the numeric code remains in the thrown error, which keeps bundle size down.
  • What does NG0203 mean and how do you fix it?
    `inject()` was called outside an injection context, such as in a callback or a method run after construction. Move the call into a field initializer or the constructor, or run it through `runInInjectionContext` with an injector you captured earlier.

saying these in an interview costs you the question

  • A bare NG code in production means the Angular build itself is broken.
  • Production builds keep full error messages; they are just hidden from the console.
  • NG0201 is fixed by importing the service's module into every component.
  • NG codes change between releases, so searching for them is pointless.
  • Runtime NG0 codes and compile-time NG8 codes come from the same source.