You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 11:09:58