首页
/ Angular Core 运行时错误机制解析:从 errors.api.md 读懂 RuntimeError 与错误码体系

Angular Core 运行时错误机制解析:从 errors.api.md 读懂 RuntimeError 与错误码体系

2026-09-08 21:30:35作者:申梦珏Efrain

导读

本文以 Angular 仓库中由 API Extractor 生成的公开 API 报告 goldens/public-api/core/errors.api.md 为主体,系统拆解 Angular @angular/core 包运行时错误处理机制的完整骨架:RuntimeError 错误类、formatRuntimeError / formatRuntimeErrorCode 格式化函数,以及包含近百个错误码的 RuntimeErrorCode 枚举。读者读完本文后,将能够准确解读浏览器控制台中形如 NG0200: ...NG0500: ... 的错误编号含义、掌握负号错误码与错误详情页之间的映射规则,理解各功能域(变更检测、依赖注入、模板、水合、信号等)的错误码区间划分,并学会在自己的开发与排错过程中快速定位对应源码与使用方式。

一、errors.api.md 是什么:一份由构建生成的 API 契约

goldens/public-api/core/errors.api.md 是 Angular 通过 API Extractor 生成的 API 报告文件(golden file),其文件头明确声明:

Do not edit this file. It is a report generated by API Extractor.

它的作用主要有三点:

  1. 公共 API 边界声明:精确记录 @angular/core 对外暴露的错误相关符号及其签名,防止贡献者在重构时无意破坏公共 API 的稳定性;
  2. 类型契约快照:任何对 RuntimeErrorformatRuntimeErrorformatRuntimeErrorCodeRuntimeErrorCode 的签名变更都会在 CI 中被 golden 测试捕获;
  3. 权威参考清单:为开发者提供一份"官方认可的"错误码全集,是排查运行时错误的索引表。

报告中声明的全部公共符号只有四个,但错误码枚举本身构成了这份文档的绝大部分价值:

符号 类型 说明
formatRuntimeError<T extends number = RuntimeErrorCode> 函数 将错误码与消息格式化为标准错误文本
formatRuntimeErrorCode<T extends number = RuntimeErrorCode> 函数 将数字错误码转换为 NG0xxx 格式字符串
RuntimeError<T extends number = RuntimeErrorCode> 运行时错误对象,继承自原生 Error,携带结构化 code
RuntimeErrorCode const enum 全部运行时错误码的常量枚举

对应实现位于 packages/core/src/errors.ts,下面逐层解读。

二、RuntimeError 类:统一错误格式的基石

2.1 类签名与构造函数

依据 errors.api.md 的声明:

// @public
export class RuntimeError<T extends number = RuntimeErrorCode> extends Error {
    constructor(code: T, message: null | false | string);
    // (undocumented)
    code: T;
}

其源码实现在 packages/core/src/errors.ts#L181-L188

export class RuntimeError<T extends number = RuntimeErrorCode> extends Error {
  constructor(
    public code: T,
    message: null | false | string,
  ) {
    super(formatRuntimeError<T>(code, message));
  }
}

几个值得注意的设计细节:

  • 泛型参数T extends number = RuntimeErrorCode,默认约束为 RuntimeErrorCode 枚举,使 error.code 具备完整的类型提示;
  • message 的三态类型null | false | string。在开发模式(ngDevMode 开启)下传入描述性字符串;在生产构建经 tree-shaking 后 message 会退化为 false,从而在产物中抹去描述信息,仅保留错误码;
  • code 是公开只读属性:可直接通过 error.code 拿到数字错误码,用于程序化判断与上报。

2.2 官方使用示例

errors.ts 的类注释给出了标准抛错范式:

throw new RuntimeError(
  RuntimeErrorCode.INJECTOR_ALREADY_DESTROYED,
  ngDevMode && 'Injector has already been destroyed.');

2.3 仓库内的真实调用场景

RuntimeError 遍布核心运行时各模块,以下是 packages/core/src/application/application_ref.ts 中的两处代表性用法:

递归 tick 检测application_ref.ts#L554-L562):

private tickImpl = (): void => {
  (typeof ngDevMode === 'undefined' || ngDevMode) && warnIfDestroyed(this._destroyed);
  if (this._runningTick) {
    profiler(ProfilerEvent.ChangeDetectionEnd);
    throw new RuntimeError(
      RuntimeErrorCode.RECURSIVE_APPLICATION_REF_TICK,
      ngDevMode && 'ApplicationRef.tick is called recursively',
    );
  }
  // ...
};

无限变更检测保护application_ref.ts#L603-L611):

if ((typeof ngDevMode === 'undefined' || ngDevMode) && runs >= MAXIMUM_REFRESH_RERUNS) {
  throw new RuntimeError(
    RuntimeErrorCode.INFINITE_CHANGE_DETECTION,
    ngDevMode &&
      'Infinite change detection while refreshing application views. ' +
        'Ensure views are not calling `markForCheck` on every template execution or ' +
        'that afterRender hooks always mark views for check.',
  );
}

再如信号输入缺省值检查(packages/core/src/authoring/input/input_signal.ts#L137):

throw new RuntimeError(RuntimeErrorCode.REQUIRED_INPUT_NO_VALUE, message);

从这些调用可以看出统一模式:枚举错误码 + ngDevMode && 短路写法,这既是错误机制的约定用法,也是生产包体积优化的关键——开发信息随 tree-shaking 被剥离。

三、错误码格式化:NG0xxx 的生成规则

formatRuntimeErrorCodeformatRuntimeError 的实现位于 packages/core/src/errors.ts#L190-L215

export function formatRuntimeErrorCode<T extends number = RuntimeErrorCode>(code: T): string {
  // Error code might be a negative number, which is a special marker that instructs the logic to
  // generate a link to the error details page on angular.io.
  // We also prepend `0` to non-compile-time errors.
  return `NG0${Math.abs(code)}`;
}

export function formatRuntimeError<T extends number = RuntimeErrorCode>(
  code: T,
  message: null | false | string,
): string {
  const fullCode = formatRuntimeErrorCode(code);

  let errorMessage = `${fullCode}${message ? ': ' + message : ''}`;

  if (ngDevMode && code < 0) {
    const addPeriodSeparator = !errorMessage.match(/[.,;!?\n]$/);
    const separator = addPeriodSeparator ? '.' : '';
    errorMessage = `${errorMessage}${separator} Find more at ${ERROR_DETAILS_PAGE_BASE_URL}/${fullCode}`;
  }
  return errorMessage;
}

3.1 规则拆解

  1. 统一前缀 NG0:无论错误码是 100 还是 -500,最终展示为 NG0100NG05000 是"core 包"在整体错误码空间中的标识(编译期错误与运行时错误通过 NG0 区分,见源码注释 "We also prepend 0 to non-compile-time errors");
  2. 负号是"有详情页"的标记Math.abs(code) 意味着负数仅用于编码语义,不影响展示数字。errors.ts 头部注释明确说明:负号表示该错误码在 angular.io 上有专门的问题排查指南,这是为了避免在运行时额外维护一份"有指南的错误码集合"而做的编码技巧;
  3. 详情页拼接:仅在 ngDevModecode < 0 时,错误消息末尾追加 Find more at <base>/<fullCode>,例如 NG0500 对应 .../errors/NG0500
  4. 标点感知:通过正则判断消息末尾是否已有句号、逗号、分号、感叹号、问号或换行,避免重复加点。

3.2 详情页基址的动态推导

ERROR_DETAILS_PAGE_BASE_URL 定义于 packages/core/src/error_details_base_url.ts,其上游 DOC_PAGE_BASE_URL 会根据当前版本动态决定域名前缀:

const full = VERSION.full;
const isPreRelease =
  full.includes('-next') ||
  full.includes('-rc') ||
  full === '0.0.0' + '-PLACEHOLDER';
const prefix = isPreRelease ? 'next' : `v${VERSION.major}`;
return `https://${prefix}.angular.dev`;

即:预发布版本(-next / -rc)链接指向 next.angular.dev,正式版指向 v<主版本号>.angular.dev。这样错误详情页始终与开发者所用的 Angular 版本语义对齐。

四、RuntimeErrorCode 全集:按功能域划分的错误码区间

errors.api.md 的核心价值在于完整罗列了 RuntimeErrorCode 枚举。其源码 packages/core/src/errors.ts#L31-L163 按功能域分组注释,core 包保留错误码区间为 100–999,并约定各包的区间如下:

错误码区间
core 100–999
forms 1000–1999
common 2000–2999
animations 3000–3999
router 4000–4999
platform-browser 5000–5500
service-worker 5600–5699
platform-server 5700–5800

下面按源码分组逐一列出全部枚举项(与 golden 文件一一对应)。

4.1 变更检测(Change Detection Errors)

错误码常量 数值
EXPRESSION_CHANGED_AFTER_CHECKED -100
RECURSIVE_APPLICATION_REF_TICK 101
INFINITE_CHANGE_DETECTION 103

其中 RECURSIVE_APPLICATION_REF_TICK(NG0101)对应上文 tickImpl 中的递归保护,INFINITE_CHANGE_DETECTION(NG0103)对应 synchronize() 中刷新轮次超限(见 application_ref.ts#L588-L612)。

4.2 依赖注入(Dependency Injection Errors)

错误码常量 数值
CYCLIC_DI_DEPENDENCY -200
PROVIDER_NOT_FOUND -201
INVALID_FACTORY_DEPENDENCY 202
MISSING_INJECTION_CONTEXT -203
INVALID_INJECTION_TOKEN -204
INJECTOR_ALREADY_DESTROYED -205
PROVIDER_IN_WRONG_CONTEXT -207
MISSING_INJECTION_TOKEN 208
INVALID_MULTI_PROVIDER -209
MISSING_DOCUMENT 210
INVALID_APP_ID 211

INJECTOR_ALREADY_DESTROYED(NG0205)正是 RuntimeError 类注释中的示例错误码;INVALID_MULTI_PROVIDER(NG0209)在 application_ref.ts#L749-L751 的 provider 校验路径中被抛出。

4.3 模板(Template Errors)

错误码常量 数值
MULTIPLE_COMPONENTS_MATCH -300
EXPORT_NOT_FOUND -301
PIPE_NOT_FOUND -302
UNKNOWN_BINDING 303
UNKNOWN_ELEMENT 304
TEMPLATE_STRUCTURE_ERROR 305
INVALID_EVENT_BINDING 306
HOST_DIRECTIVE_UNRESOLVABLE 307
HOST_DIRECTIVE_NOT_STANDALONE 308
DUPLICATE_DIRECTIVE 309
HOST_DIRECTIVE_COMPONENT 310
HOST_DIRECTIVE_UNDEFINED_BINDING 311
HOST_DIRECTIVE_CONFLICTING_ALIAS 312
MULTIPLE_MATCHING_PIPES 313
UNINITIALIZED_LET_ACCESS 314
NO_BINDING_TARGET 315
INVALID_BINDING_TARGET 316
INVALID_SET_INPUT_CALL 317
INVALID_STYLE_PROP_VALUE -318

从数值序列可看出模板错误是当前占用码位最多、增长最快的分组,其中 300–318 区间还密集覆盖了宿主指令(host directive)各类非法组合。

4.4 应用启动(Bootstrap Errors)

错误码常量 数值
MULTIPLE_PLATFORMS 400
PLATFORM_NOT_FOUND -401
MISSING_REQUIRED_INJECTABLE_IN_BOOTSTRAP 402
BOOTSTRAP_COMPONENTS_NOT_FOUND -403
PLATFORM_ALREADY_DESTROYED 404
ASYNC_INITIALIZERS_STILL_RUNNING 405
APPLICATION_REF_ALREADY_DESTROYED 406
RENDERER_NOT_FOUND 407
PROVIDED_BOTH_ZONE_AND_ZONELESS 408

ASYNC_INITIALIZERS_STILL_RUNNING(NG0405)与 APPLICATION_REF_ALREADY_DESTROYED(NG0406)均在 application_ref.ts 中被抛出(分别见第 482 行与第 799-800 行);PROVIDED_BOTH_ZONE_AND_ZONELESS(NG0408)则对应 Zone.js 与 zoneless 混用的配置冲突检测。

4.5 水合(Hydration Errors)

错误码常量 数值
HYDRATION_NODE_MISMATCH -500
HYDRATION_MISSING_SIBLINGS -501
HYDRATION_MISSING_NODE -502
UNSUPPORTED_PROJECTION_DOM_NODES -503
INVALID_SKIP_HYDRATION_HOST -504
MISSING_HYDRATION_ANNOTATIONS -505
HYDRATION_STABLE_TIMEDOUT -506
MISSING_SSR_CONTENT_INTEGRITY_MARKER -507
MISCONFIGURED_INCREMENTAL_HYDRATION 508
HYDRATION_MISSING_NODE_ON_PATH 509
PARENT_NODE_NOT_FOUND 510

这是 SSR 场景最常见的错误家族,全部集中在 500–510 区间。水合实现位于 packages/core/src/hydration,其中 hydrations/utils.tshydrations/api.ts 均直接引用了 formatRuntimeError 来格式化这些错误。

4.6 信号(Signal Errors)

错误码常量 数值
SIGNAL_WRITE_FROM_ILLEGAL_CONTEXT 600
REQUIRE_SYNC_WITHOUT_SYNC_EMIT 601
ASSERTION_NOT_INSIDE_REACTIVE_CONTEXT -602

SIGNAL_WRITE_FROM_ILLEGAL_CONTEXT(NG0600)的抛出点在 application_ref.ts#L83,用于禁止在非法的响应式上下文(如模板执行期外)中写信号。

4.7 动画、i18n、Defer 与 Standalone

分组 错误码常量 数值
动画 ANIMATE_INVALID_VALUE 650
i18n INVALID_I18N_STRUCTURE 700
i18n MISSING_LOCALE_DATA 701
Defer(750–799 区间) DEFER_LOADING_FAILED -750
Defer DEFER_IN_HMR_MODE -751
Standalone IMPORT_PROVIDERS_FROM_STANDALONE 800

DEFER_LOADING_FAILED(NG0750)在 packages/core/src/defer/instructions.ts@defer 块加载失败路径中抛出。

4.8 JIT 编译与运行时杂项(900–923)

错误码常量 数值
INVALID_DIFFER_INPUT 900
NO_SUPPORTING_DIFFER_FACTORY 901
VIEW_ALREADY_ATTACHED 902
INVALID_INHERITANCE 903
UNSAFE_VALUE_IN_RESOURCE_URL 904
UNSAFE_VALUE_IN_SCRIPT 905
MISSING_GENERATED_DEF 906
TYPE_IS_NOT_STANDALONE 907
MISSING_ZONEJS 908
UNEXPECTED_ZONE_STATE 909
UNSAFE_ATTRIBUTE_BINDING -910
VIEW_ALREADY_DESTROYED 911
COMPONENT_ID_COLLISION -912
IMAGE_PERFORMANCE_WARNING -913
UNEXPECTED_ZONEJS_PRESENT_IN_ZONELESS_MODE 914
MISSING_NG_MODULE_DEFINITION 915
MISSING_DIRECTIVE_DEFINITION 916
EXTERNAL_RESOURCE_LOADING_FAILED 918
DEF_TYPE_UNDEFINED -919
NG_MODULE_ID_NOT_FOUND 920
DUPLICATE_NG_MODULE_ID 921
VIEW_DESTROYED_INSERT_ERROR 922
VIEW_DESTROYED_MOVE_ERROR 923

注意码位 917 已被移除(源码中以 /* 917 - Removed */ 注释保留占位,避免历史错误码复用造成的歧义)。安全相关错误 UNSAFE_VALUE_IN_RESOURCE_URL(NG0904)、UNSAFE_VALUE_IN_SCRIPT(NG0905)、UNSAFE_ATTRIBUTE_BINDING(NG0910)构成 XSS 防护体系的一环,对应文档基址常量旁的 XSS_SECURITY_URL(见 error_details_base_url.ts#L35-L36)。

4.9 信号集成、Output 与 Repeater(950–956)

分组 错误码常量 数值
信号集成 REQUIRED_INPUT_NO_VALUE -950
信号集成 REQUIRED_QUERY_NO_VALUE -951
信号集成 REQUIRED_MODEL_NO_VALUE 952
Output OUTPUT_REF_DESTROYED 953
Repeater LOOP_TRACK_DUPLICATE_KEYS -955
Repeater LOOP_TRACK_RECREATE -956

REQUIRED_INPUT_NO_VALUE(NG0950)由 input.required() 缺失绑定触发,抛出点在 input_signal.ts#L137LOOP_TRACK_DUPLICATE_KEYS(NG0955)与 LOOP_TRACK_RECREATE(NG0956)由 @for 循环的 track 表达式冲突触发,实现在 packages/core/src/render3/list_reconciliation.ts

4.10 运行时依赖追踪与 resource() API(980–992)

分组 错误码常量 数值
运行时依赖追踪 RUNTIME_DEPS_INVALID_IMPORTED_TYPE 980
运行时依赖追踪 RUNTIME_DEPS_ORPHAN_COMPONENT 981
resource() MUST_PROVIDE_STREAM_OPTION 990
resource() RESOURCE_COMPLETED_BEFORE_PRODUCING_VALUE -991
resource() INVALID_RESOURCE_CREATION_IN_PARAMS 992

这一区间是最新加入的,对应新版 resource() API 与运行时依赖追踪(runtime dependency tracker)功能。至此 core 包错误码用满至 999 上限(源码注释 "Upper bounds for core runtime errors is 999")。

五、错误码语义速查:正负号与区间解读指南

5.1 负号 = 有官方错误指南

这是理解 Angular 错误输出的最重要规则。errors.ts 头部注释(packages/core/src/errors.ts#L11-L30)明确说明:

the minus sign denotes the fact that a particular code has a detailed guide on angular.io.

因此当你在开发模式看到类似 NG0500: ... Find more at .../errors/NG0500 时,说明该错误有官方深度排查文档;反之,正数错误码(如 NG0101、NG0208)通常只有代码内消息,没有独立详情页。

5.2 功能域优先定位法

排查线上错误时,可按区间快速缩小范围:

  • NG0-1xx 变更检测;
  • NG0-2xx 依赖注入;
  • NG0-3xx 模板与宿主指令;
  • NG0-4xx 应用启动与平台生命周期;
  • NG0-5xx SSR 水合;
  • NG0-6xx 信号与动画;
  • NG0-7xx i18n 与 @defer
  • NG0-8xx standalone;
  • NG0-9xx JIT、安全、信号集成、@forresource()

再结合 errors.api.md 中的常量名,即可在仓库内全局搜索对应抛错点。

六、如何在业务代码中复用这套机制

虽然 RuntimeError 主要服务于框架内部,但理解其设计对编写可维护的库与业务代码同样有借鉴意义:

  1. 用枚举管理错误码:所有错误码集中定义、按域分组、预留区间,避免魔法数字;
  2. 负号编码"附加信息":把"是否有详情文档"这类元信息编码进错误码本身,省去运行时维护额外集合的成本;
  3. ngDevMode && message 写法:开发时保留完整描述,生产构建自动剥离,兼顾可调试性与包体积;
  4. 统一格式化入口:通过 formatRuntimeError 保证所有错误输出格式一致(NG0xxx: message),并自动附带文档链接。

若需在框架外部判断错误类型,可读取 RuntimeError 实例的 code 属性,例如:

try {
  // 触发变更检测相关操作
} catch (error) {
  if (error instanceof RuntimeError) {
    switch (error.code) {
      case RuntimeErrorCode.INFINITE_CHANGE_DETECTION:
        // 处理无限变更检测
        break;
      // ...
    }
  }
}

七、延伸阅读路径

掌握这份错误码索引,就等于拿到了一张 Angular Core 运行时"故障地图":看到控制台中的 NG0xxx,即可从本文的区间表、符号名直达对应源码,快速定位问题根因。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525