- Angular
NG0203の直し方|inject()とinjection context
NG0203 は、inject() を「注入コンテキスト(injection context:Angular が部品を作っている最中の、inject() が使える時間と場所)」の外で呼んだときのエラーです。 inject() は、クラスを作るその瞬間にだけ使えます。メソッドの中や、setTimeout・await の後のように「作り終わった後」に呼ぶと、このエラーになります。
v22.2.2 で、クリック時に呼ばれるメソッドの中で inject() したときの文面です。
ERROR RuntimeError: NG0203: The `_Api45` token injection failed. `inject()` function must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`. Find more at https://v22.angular.dev/errors/NG0203
直し方の基本は、inject() をフィールド初期化子(クラスのフィールドに書く初期値)か constructor に移し、受け取ったものをメソッドで使うことです。
@Component({ selector: 'app-items', template: `<button (click)="load()">読み込む</button>` })
export class Items {
private api = inject(Api); // ← ここで受け取る(フィールド初期化子)
load() {
this.api.get(1); // ← メソッドでは受け取ったものを使う
// inject(Api).get(1); // ✕ NG0203
}
}
起きる場所と直し方を並べると、次のとおりです。いずれも v22.2.2 で再現した結果です。
| 呼んだ場所 | 結果 | 直し方 |
|---|---|---|
| フィールド初期化子・constructor | 使える | — |
| メソッド(クリック時など) | NG0203 | フィールドで受け取っておく |
ngOnInit などのライフサイクル |
NG0203 | フィールドで受け取っておく |
setTimeout・await の後 |
NG0203 | 先に受け取っておく |
関数ガードの await の後 |
NG0203 | await の前に全部受け取る |
ngOnInit での toSignal・effect・takeUntilDestroyed |
NG0203 | フィールドで書く/injector・DestroyRef を渡す |
テストの it() の中 |
NG0203 | TestBed.inject()・TestBed.runInInjectionContext() |
確認環境:Angular 22.2.2(@angular/core・@angular/router・@angular/cli)、TypeScript 6.0.3、Node.js 24.21.0(Linux)。ng new のプロジェクトで、TestBed+Vitest 5.0.4 と、ng build した画面を Chromium(Playwright)で開いて再現しました。v16・v19〜v21 の文面は、npm の @angular/core 16.2.12・19.2.25・20.3.33・21.2.25 の配布コードで確かめました(確認日 2026年10月12日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「NG0203の直し方|inject()とinjection context」
エラー文の読み方
v22.2.2 の文面は、inject() を直接呼んだときと、toSignal() などの関数の中で起きたときで、書き出しが違いました。
| 書き出し | 何が起きたか |
|---|---|
The `_Api` token injection failed. `inject()` function must be called … |
inject(Api) を外で呼んだ。_Api が受け取ろうとしたもの |
The `Router` token injection failed. … |
inject(Router) を外で呼んだ(ガードの await の後など) |
toSignal() can only be used within an injection context … |
toSignal() を外で呼んだ |
takeUntilDestroyed() can only be used within … |
引数なしの takeUntilDestroyed() を外で呼んだ |
effect() can only be used within … |
effect() を外で呼んだ |
injectApi() can only be used within … |
自作関数の assertInInjectionContext(injectApi) で検出した |
_Api の先頭の _ は、開発ビルドでクラス名に付いたもので、ソースでは Api です。token injection failed の前の名前で、どの inject() かが分かります。 can only be used の形なら、その関数を呼んだ場所を探します。
版による文面の違い
| 版 | 文面の書き出し |
|---|---|
| v16.2.12 | NG0203: inject() must be called from an injection context such as a constructor, … |
| v19.2.25〜v22.2.2 | NG0203: The `X` token injection failed. `inject()` function must be called from an injection context … |
検索でよく見る inject() must be called from an injection context は、以前の文面です。番号の NG0203 は同じなので、番号で探すと同じエラーの情報に当たります。
本番ビルドでは名前が出ない
同じ画面を本番ビルド(ng build の既定)で開くと、次だけが出ました。
ERROR y: NG0203
at Bb (http://localhost:4402/main-HMJZD2CD.js:4:2290)
どの inject() かを調べるときは、開発ビルド(ng serve)で同じ操作をします。
注入コンテキストとは
angular.dev では、注入コンテキストを次のように説明しています。
| 注入コンテキストになる場所 | 例 |
|---|---|
| DI が作るクラスの constructor の中 | @Component・@Injectable の constructor |
| そのクラスのフィールド初期化子 | private api = inject(Api); |
useFactory の関数 |
{ provide: X, useFactory: () => new X(inject(Y)) } |
InjectionToken の factory |
new InjectionToken('T', { factory: () => inject(Y) }) |
| 注入コンテキストで動く関数の中 | ガード・リゾルバー・インターセプター・runInInjectionContext |
大事なのは「どこに書いたか」ではなく「いつ実行されたか」です。 constructor の中に書いた処理でも、setTimeout や await の後に動く部分は、constructor が終わった後に実行されるので外になります。
場所別:再現したコードとエラー
① メソッドの中
load() { return inject(Api).get(1); } // クリックなどで呼ばれる
冒頭のエラーが出ました。フィールドで受け取ります。
② ngOnInit などのライフサイクル
export class InOnInit implements OnInit {
api?: Api;
ngOnInit() { this.api = inject(Api); } // ✕
}
NG0203: The `_Api` token injection failed. `inject()` function must be called from an injection context …
angular.dev でも、ライフサイクルフックの中は「インスタンスができた後なので遅すぎる」と説明されています。ngOnInit で使いたいものも、フィールドで受け取っておきます。ライフサイクルの順番は関連記事「Angularライフサイクル一覧|ngOnInitなどの実行順をログで確認」で扱っています。
③ setTimeout・Promise・await の後
constructor() {
setTimeout(() => inject(Api)); // ✕ constructor が終わった後に動く
this.init();
}
private async init() {
await Promise.resolve();
inject(Api); // ✕ await の後
}
どちらも同じ NG0203 になりました。await の前(同期で動く部分)なら使えます。 先に受け取り、後で使います。
private api = inject(Api);
private async init() {
await Promise.resolve();
this.api.get(1); // ○
}
④ 関数ガード・リゾルバーの await の後
ガードやリゾルバーの関数は、注入コンテキストの中で呼ばれます。ただし、最初の await までです。
// ✕ await の後で inject(Router)
export const badGuard: CanActivateFn = async () => {
const ok = await inject(Auth).check(); // ここまでは使える
return ok ? true : inject(Router).parseUrl('/login'); // ✕
};
NG0203: The `Router` token injection failed. `inject()` function must be called from an injection context …
// ○ 先に全部受け取る
export const authGuard: CanActivateFn = async () => {
const auth = inject(Auth);
const router = inject(Router);
const ok = await auth.check();
return ok ? true : router.parseUrl('/login');
};
直した形では、/good への遷移が通りました。Observable の pipe() の中も、値が後から届く場合(HTTP の応答など)は外になります。同期で値が流れる of(1).pipe(switchMap(…)) の中では使えましたが、これは動いた時点がたまたま中だっただけです。ガードの全体は関連記事「Angularガードの使い方|CanActivateFnでログインリダイレクト」で扱っています。
⑤ toSignal・effect・takeUntilDestroyed を ngOnInit で使う
ngOnInit() {
this.s = toSignal(of(1)); // ✕
effect(() => this.n()); // ✕
interval(1000).pipe(takeUntilDestroyed()).subscribe(); // ✕
}
NG0203: toSignal() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`.
NG0203: effect() can only be used within an injection context …
NG0203: takeUntilDestroyed() can only be used within an injection context …
これらの関数は、中で inject() を使っています(破棄のタイミングを知るため)。直し方は2つあります。
export class Fixed implements OnInit {
// 直し方1:フィールドに書く
value = toSignal(this.api.items$);
private injector = inject(Injector);
private destroyRef = inject(DestroyRef);
ngOnInit() {
// 直し方2:injector・DestroyRef を渡す
const s = toSignal(of(5), { injector: this.injector, requireSync: true });
effect(() => console.log(s()), { injector: this.injector });
interval(1000).pipe(takeUntilDestroyed(this.destroyRef)).subscribe();
}
}
2 の形で、3つともエラーなく動きました(toSignal は 5 を返しました)。toSignal の使い方は関連記事「Angular RxJS と Signals の使い分け|toSignal判断フロー」、購読の解除は関連記事「Angular unsubscribeの方法5つと要否早見表」で扱っています。
直し方の一覧
| 直し方 | 使う場面 | コード |
|---|---|---|
| フィールド初期化子 | ほとんどの場合(第一候補) | private api = inject(Api); |
| constructor | 受け取った値で初期化もしたい | constructor() { this.api = inject(Api); } |
runInInjectionContext |
後から inject() を使う関数を呼びたい |
runInInjectionContext(this.injector, () => …) |
injector を渡す |
toSignal・effect・afterNextRender など |
{ injector: this.injector } |
DestroyRef を渡す |
takeUntilDestroyed |
takeUntilDestroyed(this.destroyRef) |
assertInInjectionContext |
自作の injectXxx() 関数 |
外で呼ばれたら分かるエラーにする |
runInInjectionContext:後から注入コンテキストで動かす
import { Component, Injector, inject, runInInjectionContext } from '@angular/core';
export class Report {
private injector = inject(Injector); // 先に Injector を受け取っておく
onClick() {
runInInjectionContext(this.injector, () => {
const api = inject(Api); // ここでは使える
api.get(4);
});
}
}
runInInjectionContext(injector, fn) は、fn を、渡した injector の注入コンテキストで動かします。v22.2.2 で ngOnInit から呼んでも、中の inject(Api) が動きました。ただし、fn を async にして await した後は、やはり NG0203 になりました。 inject() を使いたい関数を、あとから呼ぶ場面(inject() 前提のライブラリ関数を、イベントの中で呼ぶなど)に使います。フィールドで受け取れるなら、そのほうがコードは短くなります。
assertInInjectionContext:自作関数で早めに分かるようにする
inject() を中で使う関数を自分で作るときは、先頭で assertInInjectionContext を呼んでおくと、外で呼ばれたときのエラーに関数名が出ます。
import { assertInInjectionContext, inject } from '@angular/core';
export function injectApi() {
assertInInjectionContext(injectApi); // 自分自身を渡す
return inject(Api);
}
NG0203: injectApi() can only be used within an injection context such as a constructor, a factory function, a field initializer, or a function used with `runInInjectionContext`.
token injection failed の文面より、どの関数を呼んだ場所が悪いのかが分かりやすくなります。
テストで出るとき
テストの it() の中で inject(Api) を直接呼ぶと、NG0203 になりました。テストでは次の2つを使います。
it('Api を使う', () => {
const api = TestBed.inject(Api); // ○ item9
TestBed.runInInjectionContext(() => {
const api2 = inject(Api); // ○ item10
});
});
TestBed.runInInjectionContext は、inject() を中で使う関数(関数ガードや injectApi() など)を、テストから直接呼ぶときに使えます。テストの書き方は関連記事「Angular テスト Vitest 入門|v22の書き方・移行・実行結果つき」で扱っています。
NullInjectorError(NG0201)との違い
| エラー | 意味 | 見るところ |
|---|---|---|
| NG0203 | inject() を呼んだ「時と場所」が外 |
どこで・いつ inject() しているか |
| NG0201 | 呼んだ場所は正しいが、提供元が無い | providers・providedIn |
NG0201 は関連記事「NullInjectorError No provider forの直し方|原因一覧」で扱っています。inject() と constructor 引数の違いは、関連記事「Angularサービスと依存性注入|injectとconstructorの違い」で扱っています。
よくある質問
Q. constructor に書いたのにエラーになります。
A. その inject() が、subscribe のコールバック・setTimeout・then・await の後にないかを見ます。constructor の中に書いても、後で動く部分は外になります。
Q. サービスのメソッドの中で inject() してもいいですか。
A. サービスでもコンポーネントと同じく、メソッドの中は外になり、providedIn: 'root' のサービスのメソッドで inject() すると NG0203 になりました。サービスもフィールド初期化子で受け取ります。
Q. インターセプターで inject() は使えますか。
A. 関数型のインターセプター(HttpInterceptorFn)は、注入コンテキストで呼ばれます。関数の先頭の inject() は動きましたが、next(req).pipe(tap(() => inject(Api))) のように応答の後で動く部分では NG0203 になりました。関数の先頭で受け取ります。インターセプターは関連記事「Angularインターセプターの使い方|トークン付与とエラー共通処理」で扱っています。
Q. ライブラリの関数(injectXxx() など)で NG0203 が出ます。
A. その関数が中で inject() を使っています。自分のコードと同じく、フィールド初期化子か constructor で呼びます。
まとめ
- NG0203 は、
inject()を作り終わった後に呼んだときのエラー。書いた場所より「いつ動いたか」で決まる - 第一候補の直し方は フィールド初期化子で受け取り、メソッドで使う
await・setTimeout・subscribeの後は外。ガード・リゾルバーでも最初のawaitの前に全部受け取るtoSignal・effect・takeUntilDestroyedは、フィールドに書くかinjector・DestroyRefを渡す- 後から動かすなら
runInInjectionContext、自作関数にはassertInInjectionContext、テストはTestBed.inject
参考・出典
確認日はいずれも 2026年10月12日です。
- Angular「NG0203:
inject()must be called from an injection context」:https://angular.dev/errors/NG0203 - Angular「Injection context」:https://angular.dev/guide/di/dependency-injection-context
- Angular「runInInjectionContext」:https://angular.dev/api/core/runInInjectionContext
- Angular「assertInInjectionContext」:https://angular.dev/api/core/assertInInjectionContext
- Angular「toSignal」:https://angular.dev/api/core/rxjs-interop/toSignal
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「NG0203の直し方|inject()とinjection context」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。