Firebase Functions部署失败:代码分析报错求助
Firebase Functions v2部署失败问题排查
添加第二个函数并重构第一个后,Firebase Functions v2无法完成部署。已绕过CI/CD手动部署,执行过tsc编译,仅重装过firebase-tools,其余环境无变化。
部署命令输出
执行firebase deploy --only functions后得到以下错误:
i deploying functions Running command: npm --prefix "$RESOURCE_DIR" run lint > lint > eslint --ext .js,.ts . Running command: npm --prefix "$RESOURCE_DIR" run build > build > tsc ✔ functions: Finished running predeploy script. i functions: preparing codebase default for deployment i functions: ensuring required API cloudfunctions.googleapis.com is enabled... i functions: ensuring required API cloudbuild.googleapis.com is enabled... i artifactregistry: ensuring required API artifactregistry.googleapis.com is enabled... ✔ functions: required API cloudbuild.googleapis.com is enabled ✔ functions: required API cloudfunctions.googleapis.com is enabled ✔ artifactregistry: required API artifactregistry.googleapis.com is enabled i functions: Loading and anaylzing source code for codebase default to determine what to deploy Serving at port 8907 shutdown requested via /__/quitquitquit Error: Functions codebase could not be analyzed successfully. It may have a syntax or runtime error
原始代码
/* eslint-disable max-len */ import "dotenv/config"; import { initializeApp } from "firebase-admin/app"; import { getFirestore } from "firebase-admin/firestore"; import { onDocumentCreated, onDocumentDeleted } from "firebase-functions/v2/firestore"; const accountSid = process.env.TWILIO_ACCOUNT_SID; const authToken = process.env.TWILIO_AUTH_TOKEN; const twilioPhoneNumber = process.env.TWILIO_PHONE_NUMBER; // eslint-disable-next-line @typescript-eslint/no-var-requires const client = require("twilio")(accountSid, authToken); export const sendSMSOnContactWrite = onDocumentCreated("/contact/{contactSubmissionId}", async (event) => { const data = event.data?.data(); console.log(data); return await client.messages.create({ // eslint-disable-next-line "body": `New contact submission from ${data?.name} at ${data?.email} with message: ${data?.message}. ${data?.name} said ${data?.hasWebsite} when asked if they have a website.${data?.hasWebsite === "yes" ? " Their website is at " + data?.websiteAddress : ""}`, "from": twilioPhoneNumber, "to": process.env.MY_PHONE_NUMBER, }); }); export const sendSMSOnContentItemDelete = onDocumentDeleted("/websites/{websiteId}/content/{contentGroupId}/data/{contentId}", async (event) => { initializeApp(); const db = getFirestore(); const websiteDocRef = db.doc("websites/" + event.params.websiteId); const websiteDoc = await websiteDocRef.get(); const websiteDocData = websiteDoc.data(); if (!websiteDocData) { // TODO: Log error return; } const users = websiteDocData["users"]; for (const userId of users) { const userDocRef = db.doc("users/" + userId); const userDoc = await userDocRef.get(); const userDocData = userDoc.data(); if (!userDocData) { // TODO: Log error return; } const deletionPreference = userDocData["preferences"]["deletion_sms"]; if (deletionPreference) { const data = event.data?.data(); console.log(data); return await client.messages.create({ "body": `Content item with title ${data?.title} was deleted from website ${websiteDocData["url"]}`, "from": twilioPhoneNumber, "to": userDocData["phone_number"], }); } else { return; } } });
问题分析与修复方案
1. Admin SDK重复初始化
Firebase Admin SDK必须在全局作用域初始化,不能在函数内部重复调用initializeApp()。函数每次触发都会执行内部代码,重复初始化会导致运行时错误。
修复:将initializeApp()和getFirestore()移到文件顶部,全局初始化:
// 全局初始化Admin SDK initializeApp(); const db = getFirestore();
2. 循环中过早返回
第二个函数的for循环里,处理第一个用户后就直接return,会导致后续用户无法被处理,同时也可能触发意外的提前终止,导致部署分析失败。
修复:
- 当用户文档不存在时,用
continue跳过当前用户,继续处理下一个 - 当用户无短信通知偏好时,用
continue跳过,而非return - 发送短信后不要立即
return,确保所有符合条件的用户都能收到通知
3. 空值未处理
代码中多处存在可选链访问(如data?.name),但未对空值情况做兜底处理,可能导致运行时错误。
修复:添加空值判断,避免后续逻辑执行失败。
4. 导入方式混用
代码中同时使用import和require导入Twilio客户端,建议统一使用ES模块语法,避免编译或运行时冲突。
修复:安装@types/twilio类型定义后,改用import导入:
import twilio from "twilio"; const client = twilio(accountSid, authToken);
修复后的完整代码
/* eslint-disable max-len */ import "dotenv/config"; import { initializeApp } from "firebase-admin/app"; import { getFirestore } from "firebase-admin/firestore"; import { onDocumentCreated, onDocumentDeleted } from "firebase-functions/v2/firestore"; import twilio from "twilio"; // 全局初始化Admin SDK initializeApp(); const db = getFirestore(); const accountSid = process.env.TWILIO_ACCOUNT_SID; const authToken = process.env.TWILIO_AUTH_TOKEN; const twilioPhoneNumber = process.env.TWILIO_PHONE_NUMBER; const client = twilio(accountSid, authToken); export const sendSMSOnContactWrite = onDocumentCreated("/contact/{contactSubmissionId}", async (event) => { const data = event.data?.data(); if (!data) { console.error("No contact submission data found"); return; } console.log(data); return await client.messages.create({ body: `New contact submission from ${data.name} at ${data.email} with message: ${data.message}. ${data.name} said ${data.hasWebsite} when asked if they have a website.${data.hasWebsite === "yes" ? ` Their website is at ${data.websiteAddress}` : ""}`, from: twilioPhoneNumber, to: process.env.MY_PHONE_NUMBER, }); }); export const sendSMSOnContentItemDelete = onDocumentDeleted("/websites/{websiteId}/content/{contentGroupId}/data/{contentId}", async (event) => { const websiteDocRef = db.doc(`websites/${event.params.websiteId}`); const websiteDoc = await websiteDocRef.get(); const websiteDocData = websiteDoc.data(); if (!websiteDocData) { console.error(`Website document ${event.params.websiteId} not found`); return; } const users = websiteDocData["users"]; if (!users || !Array.isArray(users)) { console.error("Users array is missing or invalid"); return; } for (const userId of users) { const userDocRef = db.doc(`users/${userId}`); const userDoc = await userDocRef.get(); const userDocData = userDoc.data(); if (!userDocData) { console.error(`User document ${userId} not found`); continue; } const deletionPreference = userDocData["preferences"]?.["deletion_sms"]; if (!deletionPreference) { continue; } const data = event.data?.data(); if (!data) { console.error("No deleted content data found"); continue; } console.log(data); await client.messages.create({ body: `Content item with title ${data.title} was deleted from website ${websiteDocData["url"]}`, from: twilioPhoneNumber, to: userDocData["phone_number"], }); } });
额外排查步骤
- 本地测试:使用
firebase emulators:start --only functions启动模拟器,手动触发函数,查看控制台输出的具体错误信息 - 检查编译产物:查看
dist目录下的编译后JS文件,确认无语法错误 - 验证环境变量:确保
MY_PHONE_NUMBER等环境变量已通过firebase functions:config:set配置完成,或使用Firebase Secret Manager管理敏感变量 - 更新依赖:执行
npm update firebase-functions firebase-admin确保依赖为最新版本,避免兼容问题
内容的提问来源于stack exchange,提问作者Jacob Miller
相关产品推荐
相关产品推荐

