RxDB 错误消息机制详解:RxError 错误码、参数化诊断与 DevMode 插件
2026/9/20 23:12:17 网站建设 项目流程

RxDB 错误消息机制详解:RxError 错误码、参数化诊断与 DevMode 插件

【免费下载链接】rxdbThe local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/项目地址: https://gitcode.com/gh_mirrors/rx/rxdb

RxDB 在抛出异常时不使用普通的 JavaScriptError,而是抛出带有code错误码与parameters参数的结构化RxError对象;为了让构建体积保持精简,生产包默认不含完整的人类可读错误文本,需要借助 DevMode 插件在开发期将其解锁。本文将基于当前仓库源码,剖析 RxError 的内部结构、错误码的分组命名体系、DevMode 插件如何"隧道"注入完整消息,并给出捕获、定位与处理 RxDB 错误的可运行实战方案。

RxDB 为什么默认不打包完整错误消息

打开 docs-src/docs/errors.md 可以读到官方对这一设计的解释:错误消息文本具有很高的信息熵(high entropy),压缩率很差,如果把它们全部写死在 RxDB 的构建产物里,会显著增大包体积。因此 RxDB 的默认行为是——只抛出带有正确错误码(code)和参数(parameters)的错误,而不包含完整文本

这一行为在 src/overwritable.ts 中有最直接的实现证据。overwritable对象是 RxDB 留给插件覆盖的"可覆盖点",其中默认的tunnelErrorMessage()实现为:

tunnelErrorMessage(message: string): string { return ` RxDB Error-Code: ${message}. Hint: Error messages are not included in RxDB core to reduce build size. To show the full error messages and to ensure that you do not make any mistakes when using RxDB, use the dev-mode plugin when you are in development mode: https://rxdb.info/dev-mode.html?console=error `; }

也就是说,在没有启用任何插件时,抛出的错误文本只是一句"RxDB Error-Code: XXX"加一段提示,指引开发者去启用 DevMode 插件。生产环境保持小体积,开发环境解锁完整文案,这就是 RxDB 错误消息的两段式设计:错误码常驻核心代码(几乎不占空间),错误文案通过插件按需注入。

RxError:带错误码的结构化错误对象

RxError 与 RxTypeError 的定义集中在 src/rx-error.ts 中,先看核心类型RxError

export class RxError extends Error { public code: RxErrorKey; // 错误码,如 'COL20'、'QU4'、'DB8' public message: string; // 完整消息(生产环境默认只有提示文本) public url: string; // 指向错误文档页的锚点链接 public parameters: RxErrorParameters; // 参数化诊断信息 public rxdb: true; // 恒为 true,用于识别"这是 RxDB 错误" // ... get name(): string { return 'RxError (' + this.code + ')'; } get typeError(): boolean { return false; } }

关键字段的语义如下:

字段类型说明
codeRxErrorKey字符串错误码,例如COL20DB8QU4,是全仓库统一的错误标识
parametersRxErrorParameters与本次错误相关的上下文数据对象,是排查问题的核心线索
urlstringhttps://rxdb.info/errors.html?console=errors#<code>形式的锚点文档链接
rxdbtrue恒为布尔true,是判断错误是否来自 RxDB 的可靠标记
messagestringmessageForError()拼接出的文本:错误消息 + 参数打印
typeErrorbooleanRxError恒为false;用于区分另一类RxTypeError

RxError平行的还有RxTypeError,它继承自原生TypeError(而不是Error),并带有同样的codeurlparametersrxdb字段,区别在于其typeError恒为true。从源码结构看,凡是"类型用法错误"(例如把数字传给findOne())会抛出RxTypeError,其余运行时错误抛出RxError,两者都通过newRxError(code, parameters)/newRxTypeError(code, parameters)这两个工厂函数创建。

参数如何被格式化成可读文本

RxError构造时会调用messageForError(),而参数部分由parametersToString()负责格式化。看 src/rx-error.ts 中parametersToString()的实现,它会把参数对象序列化成这样的块:

-------------------- Parameters: key1: "value1" key2: { "nested": 1 }

其中有一个值得注意的细节:当参数里包含errors数组(例如复制冲突场景下的多个子错误)时,会使用JSON.stringify(err, Object.getOwnPropertyNames(err))对每个子错误做全属性序列化,避免丢失 Error 实例上不可枚举的stack等属性——这正是为"错误里嵌套错误"这种诊断场景专门设计的。

构造函数与文档锚点

newRxError(code, parameters)内部等价于:

new RxError( code, overwritable.tunnelErrorMessage(code) + errorUrlHint(code), parameters );

即完整消息 = 当前可覆盖点提供的消息文本 + 一段"了解更多请访问"的 URL 提示。getErrorUrl()生成的锚点形如https://rxdb.info/errors.html?console=errors#COL20,也就是说每个错误码在官方错误文档页都有一个锚点,这正是本仓库 docs-src/docs/errors.md 的作用。

如何捕获并识别 RxDB 错误

得益于rxdb: true标记,识别 RxDB 错误非常简单,典型捕获代码如下:

try { await myCollection.insert(badDocument); } catch (err: any) { if (err.rxdb) { // 一定是 RxDB 抛出的错误 console.log('错误码:', err.code); console.log('诊断参数:', err.parameters); console.log('文档链接:', err.url); if (err.typeError) { // 类型用法错误(RxTypeError) } } else { // 其他 JavaScript 错误 } }

在 src/rx-error.ts 中还可以找到几个常用的判定/转换工具函数:

  • isBulkWriteConflictError(err):当底层存储返回status === 409时返回冲突错误对象,否则返回false,用于在批量写入后识别"文档写冲突";
  • rxStorageWriteErrorToRxError(err):把存储层写入错误转换为COL20错误码的RxError,其中存储状态码与消息的映射关系为409 → document write conflict422 → schema validation error510 → attachment data missing
  • newRxFetchError(response, additionalParameters):把失败的fetch()响应转换为FETCH错误码的RxError,并在parameters中带上urlstatusstatusTexterrorText等网络诊断信息。

错误码的分组命名体系

RxDB 的错误码不是随机的,而是带有可读前缀的分组体系。文档站渲染完整错误列表的组件 docs-src/src/components/error-messages.tsx 中维护了一份前缀映射表,从中可以清楚地看到"前缀 → 所属模块"的对应关系:

前缀所属模块前缀所属模块
UTutil / configCOLrx-collection
PLpluginsDOCrx-document
QUrx-queryDMdata-migrator
MQmqueryATattachments
DBrx-databaseENencryption
WMCPwebmcpJDjson-dump
CONFLICTrx-collection(文档更新冲突)LDlocal-documents
RC*replication 系列SCdev-mode check-schema
DVMdev-modeVDvalidate
GQLreplication-graphqlCRDTcrdt
DXEstorage-dexieSQLstorage-sqlite
RMstorage-remoteMGreplication-mongodb
Rreact 插件GDR/ODRgoogle-drive / onedrive 复制
FETCHfetch 网络请求SNH"should never happen" 内部哨兵

例如看到QU5就知道是查询排序问题(sort 字段未定义在 schema 中),看到EN2就知道是加密密码长度不足,看到RC_COUCHDB_1就知道是 CouchDB 复制 URL 缺少末尾斜杠。这种前缀命名让错误码本身就成为"模块级分类标签"。

每条错误的四要素:message / cause / fix / docs

每个错误码在 src/plugins/dev-mode/error-messages.ts 中都对应一个对象,统一包含四个字段:

QU5: { message: 'RxQuery.sort(): does not work because key is not defined in the schema', cause: 'The field used for sorting is not defined in the schema.', fix: 'Add the field to the schema or sort by a different field.', docs: 'https://rxdb.info/rx-query.html?console=errors&code=QU5#sort' }
  • message:一句话概括错误;
  • cause:解释为什么会发生;
  • fix:给出可执行的修复建议;
  • docs:指向对应功能文档的锚点链接。

DevMode 插件会把这四要素全部拼进抛出的错误消息中,这也是开发期排错效率高的根本原因。

用 DevMode 插件解锁完整错误消息

DevMode 插件(开发模式插件)是解锁完整错误文案的关键。安装方式为引入RxDBDevModePlugin并注册:

import { RxDBDevModePlugin } from 'rxdb/plugins/dev-mode'; import { addRxPlugin } from 'rxdb/plugins/core'; addRxPlugin(RxDBDevModePlugin);

该插件的定义在 src/plugins/dev-mode/index.ts,它通过overwritable.tunnelErrorMessage()覆盖默认实现:从ERROR_MESSAGES表中取出对应错误码的messagecausefixdocs,拼接成如下格式的完整消息:

Error message: RxQuery.sort(): does not work because key is not defined in the schema Error code: QU5 Cause: The field used for sorting is not defined in the schema. Fix: Add the field to the schema or sort by a different field. Docs: https://rxdb.info/rx-query.html?console=errors&code=QU5#sort

值得注意的是,DevMode 插件在注册时还会主动校验ERROR_MESSAGES表中是否存在传入的错误码,若不存在会直接抛出Error-Code X not known, contact the maintainer——这保证了任何由核心代码抛出的错误码都必然有对应的消息条目(完整的 1395 行错误码表见 src/plugins/dev-mode/error-messages.ts)。

DevMode 插件应在开发环境启用、生产环境禁用,官方文档 docs-src/docs/dev-mode.md 给出了几种按环境条件加载的推荐写法:

Node.js 环境(按NODE_ENV条件动态导入):

async function createDb() { if (process.env.NODE_ENV !== "production") { await import('rxdb/plugins/dev-mode').then( module => addRxPlugin(module.RxDBDevModePlugin) ); } const db = await createRxDatabase( /* ... */ ); }

Angular 环境(复用 Angular 的isDevMode()):

import { isDevMode } from '@angular/core'; async function createDb() { if (isDevMode()) { await import('rxdb/plugins/dev-mode').then( module => addRxPlugin(module.RxDBDevModePlugin) ); } const db = await createRxDatabase( /* ... */ ); }

webpack 环境(配合DefinePlugin注入的编译期常量):

// webpack.config.js module.exports = { // ... plugins: [ new webpack.DefinePlugin({ MODE: JSON.stringify("production") }) ] };
declare var MODE: 'production' | 'development'; async function createDb() { if (MODE === 'development') { await import('rxdb/plugins/dev-mode').then( module => addRxPlugin(module.RxDBDevModePlugin) ); } const db = await createRxDatabase( /* ... */ ); }

动态import()+ 环境判断的写法可以让打包器在 production 分支下直接 tree-shake 掉整个 DevMode 插件,保证生产构建不含这些额外检查与消息文本。

关闭 DevMode 的警告与控制台提示

DevMode 插件激活时会在控制台打印一段醒目的console.warn()警告(提示你确认没有在 production 误用),如果你已了解这一点,可以调用disableWarnings()关闭它(见 src/plugins/dev-mode/index.ts 中disableWarnings()的定义):

import { disableWarnings } from 'rxdb/plugins/dev-mode'; disableWarnings();

另外,在 localhost 浏览器环境下插件会向 DOM 注入一个用于统计营销效果的 tracking iframe,官方文档说明:拥有 premium 权限的开发者可以在创建数据库前调用setPremiumFlag()来禁用该 iframe。

DevMode 插件附带的其他开发期检查

除了注入完整错误消息,DevMode 插件还在 src/plugins/dev-mode/index.ts 中挂载了大量 hooks,把开发期校验做成了"全链路"的:

  • Schema 检查preCreateRxSchemacheckSchema):校验字段命名、主键、索引、加密字段、默认值、版本号等,对应SC1~SC43系列错误码;
  • 数据库/集合命名检查preCreateRxDatabasepreCreateRxCollection):ensureDatabaseNameIsValid()ensureCollectionNameValid(),以及集合名不能以下划线_开头(DB2);
  • ORM 方法检查createRxCollectioncheckOrmMethods):校验 statics、methods、attachments 上的自定义方法命名与类型;
  • 查询检查preCreateRxQuerycheckQueryprePrepareQuerycheckMangoQuery):校验查询字段是否存在于 schema、索引是否合法、findOne().limit()等非法链式调用;
  • 文档主键检查createRxDocumentensurePrimaryKeyValid):主键不可修改、不可带空白/换行/双引号等(DOC18~DOC24);
  • 存储校验器要求preCreateRxDatabase):当 DevMode 开启时,存储层必须使用validate-前缀的 schema 校验器(如wrappedValidateAjvStorage()),否则抛出DVM1,因为"大部分 RxDB 使用问题都源于写入了不合 schema 的数据";
  • 对象深度冻结deepFreezeWhenDevMode()会对文档对象做深度Object.freeze(),让开发期意外修改只读对象的行为立即报错——源码注释明确说明 deep-freeze 与 deep-clone 性能相当,所以这个能力只在 dev-mode 生效。

从这些 hooks 可以看出:DevMode 不仅是错误消息的"开关",更是 RxDB 的整套开发期自检系统,这也正是官方强调"开发期必须使用、生产期禁止使用"的原因。

高频错误码速查

完整的错误码清单以ERROR_MESSAGES对象维护在 src/plugins/dev-mode/error-messages.ts(共 1395 行,覆盖UTSNH的全部模块),并在文档页以分组列表形式渲染。以下按模块摘录高频错误码,便于快速定位问题:

数据库与集合(DB / COL)

错误码消息要点常见场景
DB2集合名不能以下划线_开头addCollections()命名违规
DB6另一实例用不同 schema 创建了同名集合修改 schema 后未递增版本号
DB8同名的同名+storage 数据库已存在重复createRxDatabase(),可用ignoreDuplicate(仅 dev-mode)或closeDuplicates
DB13数据库/集合名含美元符号$命名违规
COL1不能插入已存在的文档应用upsert()
COL5/COL6find()/findOne()参数错误按 ID 查询应使用findOne(id)
COL20存储写入错误底层存储返回 409/422/510
COL21集合已被关闭或移除跨 tab 或跨 realm 访问已销毁集合
CONFLICT文档更新冲突:必须基于上一个 revision 修改多端并发写同一文档

查询(QU / MQ)

错误码消息要点常见场景
QU4主键字段上不能使用.regex()查询引擎限制
QU5sort 字段未定义在 schema 中排序字段拼写错误或未建模
QU6findOne()不能调用.limit()非法链式调用
QU10throwIfMissing: true但结果为空exec(true)/remove(true)未命中文档
QU11查询对象不是合法的 Mango 查询查询键非法
QU12使用的 index 不在 schema 中索引未定义
QU16$regex必须使用字符串而非 RegExp 实例正则对象不可 JSON 序列化
QU17findByIds()上不能链式调用查询方法请改用find()

文档(DOC)

错误码消息要点常见场景
DOC1数组元素无法被get$观察观察整个数组字段
DOC8/DOC19主键不可修改主键不可变,需新建文档
DOC9final 字段不可修改schema 中final: true的字段
DOC16set()只能用于临时文档请改用update()/patch()/modify()
DOC18复合主键缺少字段复合主键字段必须齐全
DOC20文档缺少主键插入前补全主键
DOC24文档数据无法结构化克隆传入了 Date/Function/Proxy 等非纯 JSON 数据

Schema(SC)

错误码消息要点常见场景
SC1字段名不匹配正则字段命名违规
SC6主键只能定义在顶层主键位置错误
SC30schema 必须定义 primaryKeyschema 缺主键
SC34用于索引的 string 字段必须设置maxLength索引字段缺约束
SC35用于索引的 number 字段必须设置multipleOf索引字段缺约束
SC39主键必须设置maxLength主键约束缺失
SC42主键/索引字符串的maxLength不能超过 2048过大索引影响性能
SC43加密字段不能嵌套在其他加密字段内父路径加密已覆盖子路径

复制(RC 系列)

错误码消息要点常见场景
RC_PULL/RC_PUSH/RC_STREAM复制 pull/push/流处理器抛错查看.errors可观察对象
RC_COUCHDB_1CouchDB URL 必须以/结尾URL 格式错误
RC_OUTDATED客户端版本过旧,复制被取消客户端需要升级
RC_UNAUTHORIZED/RC_FORBIDDEN未授权 / 行为违规被取消检查鉴权 headers 与权限
RC_WEBSOCKET_TIMEOUTWebSocket 连接超时检查服务端可达性

工具与配置(UT / EN / AT / VD 等)

错误码消息要点常见场景
UT1名称必须是非空字符串数据库名传参错误
UT5schema 开了 keyCompression 但存储不支持需加 key-compression 插件
UT6schema 含加密字段但存储无加密处理器需加 encryption 插件
EN2密码长度不足(min 12)加密密码过短
EN3schema 加密但未提供 password创建数据库时缺密码
AT1使用附件前需在 schema 中启用附件未开启
VD2文档对象不匹配 schema写入了不合 schema 的数据
FETCHfetch 请求失败网络/服务端问题
SNH"This should never happen"内部哨兵错误,遇到即应提 issue

在仓库源码中定位错误码的抛出点

如果想知道某个错误码在哪里被抛出,直接在整个src目录下搜索newRxError('CODE'newRxTypeError('CODE'即可。例如COL20由 src/rx-error.ts 中的rxStorageWriteErrorToRxError()统一抛出;DB2DB4由 src/plugins/dev-mode/index.ts 的preCreateRxCollectionhook 抛出;QU4QU6等查询类错误码则散布在 src/rx-query.ts 及查询构建相关文件中。错误码→消息→抛出点的三方对照,构成了完整的排错链路:

  1. 捕获RxError,读取err.codeerr.parameters
  2. 对照本文速查表或 src/plugins/dev-mode/error-messages.ts 中的cause/fix
  3. 打开err.url指向的文档锚点;
  4. 如需深入,在src下搜索newRxError('CODE'定位抛出逻辑。

小结

RxDB 的错误处理是一套为"小体积 + 可诊断"双重目标设计的参数化体系:生产构建只携带低熵的错误码与结构化参数,DevMode 插件则在开发期把四要素(message/cause/fix/docs)完整注入错误对象,并顺带启用 schema、查询、ORM、主键、对象冻结等一整套开发期自检。掌握err.codeerr.parameterserr.rxdberr.typeError这几个字段的用法,配合错误码前缀分组与源码搜索,即可在生产与开发两种模式下都快速定位并修复 RxDB 相关问题。

【免费下载链接】rxdbThe local-first database that runs on every JS runtime and replicates with your existing backend - no vendor, no lock-in - https://rxdb.info/项目地址: https://gitcode.com/gh_mirrors/rx/rxdb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询