Cloud Functions中Firestore update抛出13 INTERNAL错误求助
问题场景
在Cloud Functions中调用Firestore的update方法时,出现以下错误:
---updateUserByID finish update--- Caused by: Error at WriteBatch.commit (/workspace/node_modules/firebase-admin/node_modules/@google-cloud/firestore/build/src/write-batch.js:433:23) at DocumentReference.update (/workspace/node_modules/firebase-admin/node_modules/@google-cloud/firestore/build/src/reference.js:433:14) at Object.updateUserByID (/workspace/dist/src/DB/db.js:74:14) at createSpecialistOrderListService (/workspace/dist/src/crud/specialist/services/specialistList/createSpecialistOrderListService.js:38:29) at runMicrotasks (<anonymous>) at processTicksAndRejections (node:internal/process/task_queues:96:5) at async getRecommendedSpecialistsListController (/workspace/dist/src/crud/specialist/controllers/getRecommendedSpecialistsListController.js:25:44) at async /workspace/dist/src/framework/express/middlewares/express-handler.js:18:36 Error Details: - Code: `13` - Description: `Received RST_STREAM with code 1` - Metadata: `{ internalRepr: Map(0) {}, options: {} }` - Note: `Exception occurred in retry method that was not classified as transient`
触发代码:
const writeResult = await admin .firestore() .collection(FirestoreCollections.Users) .doc(userID) .update(fieldsToUpdate);
异常现象:
- 该方法在其他Cloud Functions中正常运行;
- 部分场景下,抛出错误但Firestore数据已完成更新;
- 本地测试问题复现;
- 新建Cloud Functions使用相同方法完全正常。
排查思路
检查
fieldsToUpdate格式合法性
Firestore的DocumentReference.update()方法接受的是键值对对象,而非数组。你提供的fieldsToUpdate示例是数组,这会导致SDK内部处理异常,可能引发请求中断,同时部分操作意外执行。对比依赖版本差异
对比出问题的函数与正常运行的新函数的firebase-admin及嵌套的@google-cloud/firestore版本。错误栈显示使用的是嵌套依赖(firebase-admin/node_modules/@google-cloud/firestore),可能存在版本不兼容或依赖安装不一致的情况。排查函数执行上下文
检查出问题的函数是否有特殊中间件、超时配置或并发逻辑。比如express-handler中间件是否修改了请求上下文,导致Firestore客户端实例异常;是否存在函数内重复初始化admin实例的情况。验证Firestore文档状态
确认出错的userID对应的文档是否存在,是否有并发写入冲突,或者文档大小是否接近1MB的限制(过大的文档更新可能引发传输中断)。重试机制异常排查
错误提示“Exception occurred in retry method that was not classified as transient”,说明SDK的重试逻辑未将RST_STREAM错误识别为可重试的临时错误,导致重试失败抛出异常。
解决方案
修正
fieldsToUpdate格式
将数组格式改为键值对对象。如果是要更新某个数组字段,使用Firestore的FieldValue方法:// 正确格式:更新指定字段为数组 const fieldsToUpdate = { targetArrayField: [ { "boolean": true, "number": 100, "id": "some_id" } ] }; // 或向数组添加元素(避免覆盖原有数据) const fieldsToUpdate = { targetArrayField: admin.firestore.FieldValue.arrayUnion( { "boolean": true, "number": 100, "id": "some_id" } ) };统一依赖版本
- 升级
firebase-admin到最新稳定版,或与正常运行的新函数保持相同版本; - 删除
node_modules和package-lock.json后重新安装,确保依赖树一致,避免嵌套版本冲突; - 在
package.json中明确锁定@google-cloud/firestore版本,避免依赖自动升级。
- 升级
添加幂等性处理
由于部分场景下数据已更新,需保证操作幂等:- 使用事务执行更新,确保原子性;
- 为更新操作添加条件判断,比如:
await admin.firestore() .collection(FirestoreCollections.Users) .doc(userID) .update(fieldsToUpdate, { merge: true });
调整Firestore客户端重试配置
手动配置重试参数,将RST_STREAM错误标记为可重试:const firestore = admin.firestore({ retry: { retryDelayMultiplier: 1.5, totalTimeout: 60000, retryableErrorCodes: ['13', 'UNKNOWN'] } });检查函数初始化逻辑
确保admin实例在函数外部初始化(全局作用域),避免每次请求重复初始化:// 全局作用域初始化 const admin = require('firebase-admin'); admin.initializeApp(); const firestore = admin.firestore(); // 函数内部使用已初始化的实例 exports.yourFunction = async (req, res) => { // ... 使用firestore进行操作 };
内容的提问来源于stack exchange,提问作者maylorsan

