iOS应用Firebase云函数异步推送通知报错:Requested entity was not found
异步推送iOS静默通知时遇到「Requested entity was not found」错误
问题背景
现有一款接收推送通知的iOS应用,后端采用Node.js结合Firebase云函数实现。原有同步推送通知函数资源损耗过高,改成异步实现后出现报错,以下是测试静默通知的代码及错误日志。
测试代码
var allMesseges = [] const sendAllMessages = async () => { const sendMessege = allMesseges.map(async (message) => { let response = await admin.messaging().send(message); }); await Promise.all(sendMessege); }; exports.allSilentPushTrigerAsync = functions.database.ref('/development/allSilentPushTrigerAsync/').onWrite(async event => { let snap = await db.ref("users").once("value") let users = snap.val() for (const [key, user] of Object.entries(users)) { if (user.details.pushToken) { const message = { apns: { headers: { "apns-push-type": "background", "apns-priority": "5", }, payload: { "aps": { "content-available": 1 }, }, }, token: user.details.pushToken, }; allMesseges.push(message) } } await sendAllMessages() })
错误日志
allSilentPushTrigerAsync pzhv27hvepg4 Error: Requested entity was not found. at Function.fromServerError (/workspace/node_modules/firebase-admin/lib/utils/error.js:254:16) at createFirebaseError (/workspace/node_modules/firebase-admin/lib/messaging/messaging-errors-internal.js:35:47) at /workspace/node_modules/firebase-admin/lib/messaging/messaging-api-request-internal.js:79:75 at processTicksAndRejections (node:internal/process/task_queues:96:5) at async /workspace/index.js:180:28 at async Promise.all (index 4) at async sendAllMessages (/workspace/index.js:182:9) at async /workspace/index.js:207:10 at async /workspace/index.js:180:28 对应代码行: let response = await admin.messaging().send(message); at async /workspace/index.js:207:10 对应代码行: await sendAllMessages()
问题分析
这个报错是FCM的典型错误,核心原因是推送token无效:
- token已过期、被苹果APNs标记为无效(比如用户卸载App、设备重置、App更新后token未同步到后端)
- token格式错误,或者不属于当前Firebase项目的有效推送token
另外你的代码还有几个潜在问题:
- 全局变量
allMesseges(拼写错误,应为allMessages)在云函数实例复用场景下会导致消息累积,引发重复推送 - 没有错误处理,单个token推送失败会导致整个
Promise.all中断,其他用户无法收到推送 - 循环调用
send效率低下,FCM提供了更适合批量推送的sendMulticast方法
解决方案
1. 修复代码问题,改用批量推送
把全局变量移到函数内部,增加错误处理,用sendMulticast替代循环send:
exports.allSilentPushTrigerAsync = functions.database.ref('/development/allSilentPushTrigerAsync/').onWrite(async (event) => { const messages = []; const snap = await db.ref("users").once("value"); const users = snap.val(); // 收集有效推送消息 for (const [key, user] of Object.entries(users)) { if (user.details?.pushToken) { // 用可选链避免属性不存在报错 const message = { apns: { headers: { "apns-push-type": "background", "apns-priority": "5", }, payload: { "aps": { "content-available": 1 }, }, }, token: user.details.pushToken, }; messages.push(message); } } // 批量发送并处理结果 if (messages.length > 0) { const response = await admin.messaging().sendMulticast({ messages }); // 记录失败的推送,并收集无效token const invalidTokens = []; response.responses.forEach((resp, idx) => { if (!resp.success) { console.error(`推送失败,token: ${messages[idx].token},错误详情: ${resp.error.message}`); // 判断是否是token失效的错误 if (resp.error.code === 'messaging/registration-token-not-registered') { invalidTokens.push(messages[idx].token); } } }); // 可选:批量清理后端的无效token(根据你的数据结构调整) if (invalidTokens.length > 0) { const cleanTasks = invalidTokens.map(token => { return db.ref("users") .orderByChild('details/pushToken') .equalTo(token) .once('value') .then(snap => { snap.forEach(child => { child.ref.update({ 'details/pushToken': null }); }); }); }); await Promise.all(cleanTasks); } } });
2. 日常维护建议
- 每次App启动时,获取最新的推送token并同步到后端,覆盖旧token
- 定期清理后端存储的无效token,减少无效推送请求
- 推送时增加重试机制,针对临时错误(比如网络波动)可以重试1-2次
内容的提问来源于stack exchange,提问作者Миша Гарлинский
相关产品推荐
相关产品推荐

