---
title: "NG0203: por que o inject() falha fora do contexto de injeção"
description: "Por que o Angular estoura o NG0203 quando o inject() roda fora do contexto de injeção, a janela exata em que o inject() é válido, as armadilhas comuns (subscribe, route guard async, método de classe, getter), e os fixes ranqueados do field initializer ao runInInjectionContext."
deck: "O `NG0203` quer dizer que você chamou o [`inject()`](https://angular.dev/api/core/inject) depois que o Angular terminou de montar a classe. O contexto de injeção fica aberto só durante a construção: num field initializer, no corpo do constructor, ou dentro do [`runInInjectionContext`](https://angular.dev/api/core/runInInjectionContext). Chame mais tarde, de um `subscribe`, de um `setTimeout` ou de um getter, e ele estoura. Fonte: [angular.dev/errors/NG0203](https://angular.dev/errors/NG0203)."
author: "André Ramos"
url: "https://andreramos.dev/pt/angular/angular-ng0203-inject-outside-injection-context/"
lang: "pt-BR"
type: "article"
---

# NG0203: por que o inject() falha fora do contexto de injeção

> O `NG0203` quer dizer que você chamou o [`inject()`](https://angular.dev/api/core/inject) depois que o Angular terminou de montar a classe. O contexto de injeção fica aberto só durante a construção: num field initializer, no corpo do constructor, ou dentro do [`runInInjectionContext`](https://angular.dev/api/core/runInInjectionContext). Chame mais tarde, de um `subscribe`, de um `setTimeout` ou de um getter, e ele estoura. Fonte: [angular.dev/errors/NG0203](https://angular.dev/errors/NG0203).

## A janela, e quando ela fecha

A mensagem completa é `inject() must be called from an injection context`. O contexto de injeção é a janela de runtime em que o injector atual está ao alcance, e a doc é exata sobre quando ele existe: durante a construção de uma classe que o sistema de DI instancia, nos field initializers dessa classe, numa factory de `useFactory` ou de `InjectionToken`, e dentro de qualquer stackframe que roda via `runInInjectionContext`. Essa é a lista inteira.

A pegadinha é que a janela é estreita. Ela fica aberta enquanto o Angular monta a instância e fecha no instante em que a construção termina. A referência da API diz isso sem rodeio: chamadas ao `inject()` ficam proibidas depois que a instância já foi criada, em métodos, incluindo os lifecycle hooks. Então a pergunta nunca é *consigo injetar esse tipo*, porque o provider existe. A pergunta é *o contexto de injeção ainda estava aberto quando essa linha rodou*.

## Onde o inject() funciona, e onde ele estoura

A maioria dos NG0203 vem de um número pequeno de lugares de chamada. O padrão se repete: código que roda de forma síncrona enquanto o Angular monta a classe passa, e código adiado pra um callback, um timer, um stream ou o corpo de um método já perdeu a janela.

Leia a tabela pelo lugar de chamada, não pelo tipo. O tipo é injetável em todas as linhas. O que muda é se a linha executa dentro da janela de construção ou depois dela.

| Lugar de chamada | inject() aqui? | Por quê |
|---|---|---|
| Field initializer (`private x = inject(X)`) | Funciona | Roda durante a construção, o contexto está aberto. |
| Corpo do constructor | Funciona | Ainda dentro da construção. |
| Factory de `useFactory` / `InjectionToken` | Funciona | As factories rodam dentro do contexto de DI. |
| Dentro de `runInInjectionContext(injector, fn)` | Funciona | O injector fica disponível pra aquele stackframe. |
| Corpo de lifecycle hook (`ngOnInit`) | Estoura NG0203 | Roda depois que a instância foi criada. |
| Callback de `subscribe` / `.then` / `setTimeout` | Estoura NG0203 | Trabalho adiado, o contexto fechou faz tempo. |
| Método de classe ou getter | Estoura NG0203 | Chamado depois da construção, fora da janela. |
| Event handler (alvo de `(click)`) | Estoura NG0203 | Dispara na ação do usuário, muito depois da construção. |

## As armadilhas que parecem inofensivas

A primeira armadilha é o `inject()` dentro de um `subscribe`. O constructor parece certo, o lugar da chamada parece certo, mas o callback roda quando o valor chega, não quando a classe é montada, e a essa altura o contexto já fechou.

A segunda é o route guard async. Um `CanActivateFn` roda num contexto de injeção quando começa, então o primeiro `inject()` passa. Coloque um `await` antes do próximo e você saiu da janela síncrona: a doc afirma que o inject só dá pra usar de forma síncrona e não pode ser usado depois de nenhum await. A terceira é a silenciosa. Um getter privado ou um método auxiliar que chama `inject()` se parece com código de field, mas executa no acesso, muito depois da construção.

*Três lugares de chamada que estouram NG0203*

```ts
export class OrdersComponent {
  private readonly orders = inject(OrderService);

  ngOnInit() {
    this.orders.changes$.subscribe(() => {
      // NG0203: o callback roda depois da construção
      const logger = inject(LoggerService);
      logger.track('orders changed');
    });
  }

  // NG0203: o getter roda no acesso, não durante a construção
  get currency() {
    return inject(LOCALE_ID) === 'pt-BR' ? 'BRL' : 'USD';
  }
}
```

## Os fixes, ranqueados

Comece no topo desta lista e só desça quando o caso te obrigar. A maior parte do NG0203 some no primeiro passo, porque a referência injetada não precisava ser resolvida tarde, e sim o código foi escrito tarde.

Primeiro, mova o `inject()` pra um field initializer ou capture a referência uma vez na construção e use depois. O field guarda a dependência; o callback usa o field. Segundo, quando você precisa mesmo resolver dentro de código adiado (um token escolhido em runtime, um injector por chamada), capture o `Injector` na construção e chame `runInInjectionContext(injector, () => inject(X))` onde o trabalho roda. Terceiro, pro caso específico de cleanup de RxJS, o `takeUntilDestroyed` aceita um `DestroyRef` explícito, então dá pra capturar o `inject(DestroyRef)` uma vez e passar pro operador fora do contexto.

Eu recusaria o fix reflexo de embrulhar toda chamada tardia em `runInInjectionContext`. Ele silencia o NG0203, mas em geral significa que o design adiou uma decisão que era da construção. Quando vejo isso em revisão, pergunto o que de fato é dinâmico ali. Se a resposta é *nada*, a referência deveria ser um field, e não uma busca em runtime.

*Capture na construção, use no callback*

```ts
export class OrdersComponent {
  private readonly orders = inject(OrderService);
  // Capture uma vez, dentro da janela de construção:
  private readonly logger = inject(LoggerService);
  private readonly destroyRef = inject(DestroyRef);

  ngOnInit() {
    this.orders.changes$
      .pipe(takeUntilDestroyed(this.destroyRef))
      .subscribe(() => this.logger.track('orders changed'));
  }
}
```

## O runInInjectionContext, e o await que quebra ele

Quando a busca é mesmo adiada, injete o `Injector` (ou o `EnvironmentInjector`) na construção e reabra o contexto onde você precisar. A assinatura é `runInInjectionContext(injector, fn)`: ele roda a `fn` com aquele injector disponível, então o `inject()` funciona dentro da closure e devolve o resultado dela.

Tem um detalhe que engana muita gente, e vale dizer em voz alta: o contexto que o `runInInjectionContext` abre é síncrono. No momento em que você dá um `await` dentro da closure, você cai fora dele, e um `inject()` depois do await estoura NG0203 de novo. Então resolva tudo o que precisa antes do primeiro await, e só depois faça o trabalho assíncrono. Essa é a mesma regra que pega os route guards async, e é o motivo mais comum de um fix com `runInInjectionContext` ainda falhar.

*Injete o Injector, reabra o contexto depois*

```ts
export class ReportExporter {
  private readonly injector = inject(Injector);

  exportLater() {
    setTimeout(() => {
      runInInjectionContext(this.injector, () => {
        // Resolva de forma síncrona, antes de qualquer await:
        const http = inject(HttpClient);
        const url = inject(REPORT_URL);
        http.get(url).subscribe();
      });
    }, 1000);
  }
}
```

## Artefato reaproveitável: Resolvendo o NG0203 de inject() fora do contexto de injeção

- Confirme o lugar de chamada: um field initializer, o constructor ou uma factory está dentro da janela; um callback, método, getter ou event handler está fora dela.
- Se a referência não é de fato dinâmica, mova o inject() pra um field initializer e use o field no callback.
- Capture o inject(DestroyRef) na construção e passe pro takeUntilDestroyed pra dar unsubscribe fora do contexto.
- Quando a resolução tem que ser adiada, injete o Injector na construção e embrulhe o código tardio em runInInjectionContext(injector, fn).
- Dentro do runInInjectionContext, resolva todo inject() antes do primeiro await; o contexto é síncrono e um await te tira dele.
- Trate um runInInjectionContext reflexo como cheiro de problema: se nada é dinâmico, a dependência é de um field, e não de uma busca em runtime.

## Perguntas frequentes

### O que o NG0203 quer dizer no Angular?

O NG0203 quer dizer que o inject() rodou fora de um contexto de injeção. A mensagem completa é 'inject() must be called from an injection context'. O contexto de injeção fica aberto só enquanto o Angular monta a classe (num field initializer, no constructor, numa factory ou dentro do runInInjectionContext) e fecha quando a construção termina.

### Onde dá pra chamar o inject() sem estourar NG0203?

Num field initializer, no corpo do constructor, numa factory de useFactory ou InjectionToken, e dentro do runInInjectionContext. Ele estoura no corpo de lifecycle hooks, em callbacks de subscribe e setTimeout, em métodos de classe, em getters e em event handlers, porque esses rodam depois da construção.

### Como eu uso o inject() dentro de um subscribe ou setTimeout?

Capture a dependência na construção (um field initializer) e use o field no callback. Se a resolução precisa acontecer tarde, injete o Injector na construção e chame runInInjectionContext(injector, () => inject(X)) onde o código adiado roda.

### Por que o inject() ainda estoura NG0203 depois de um await?

O contexto de injeção, inclusive o que o runInInjectionContext abre, é síncrono. Depois de um await você já saiu dele, então um inject() mais tarde estoura. Resolva tudo o que precisa antes do primeiro await, e só depois faça o trabalho assíncrono.

## Fontes consultadas

- https://angular.dev/errors/NG0203
- https://angular.dev/api/core/inject
- https://angular.dev/api/core/runInInjectionContext
- https://angular.dev/guide/di/dependency-injection-context
- https://angular.dev/guide/di
- https://angular.dev/api/core/rxjs-interop/takeUntilDestroyed
- https://angular.dev/api/core/DestroyRef

## Leia também

- [NG0200: dependência circular na injeção de dependências](https://andreramos.dev/pt/angular/angular-ng0200-circular-dependency-di/)
- [NG0950: lendo um input obrigatório antes do Angular preencher](https://andreramos.dev/pt/angular/angular-ng0950-required-input-not-set/)
- [Facade Pattern no Angular: quando o componente sabe demais](https://andreramos.dev/pt/angular/facade-pattern-in-angular/)
