Firebase Cloud Function - RangeError: Maximum call stack size exceeded

Firebase Cloud Function - RangeError: Maximum call stack size exceeded

我正在尝试写一个 onCall Firebase Cloud Function that calls an external Zoho Desk API 到 return 支持票列表。

但每当我调用我的 Firebase 云函数时,它 return 都会出现此错误:Unhandled error RangeError: Maximum call stack size exceeded

我找到的大多数其他答案都与 Firebase 文档快照有关,他们说这是由无限循环引起的。但我不确定如何将这些知识应用到我的外部 api 调用中。

这里是有问题的云函数:

export const getZohoDeskTickets = functions
  .region('us-central1')
  .https.onCall(async (data, context) => {
    // Check if it passed App Check
    if (context.app == undefined) {
      throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called from an App Check verified app.'
      );
    }

    // Check the authentication
    if (!context.auth) {
      // Throwing an HttpsError so that the client gets the error details.
      throw new functions.https.HttpsError(
        'unauthenticated',
        'The function must be called while authenticated.'
      );
    }

    // Do the operation
    const zohoAccessToken = await getSecretVersion(
      'zoho-self-client-api-access-token'
    ).catch((error) => {
      throw new functions.https.HttpsError('unknown', error.message, error);
    });
    return axios
      .get('https://desk.zoho.com/api/v1/tickets', {
        headers: {
          orgId: '123456789',
          Authorization: `Zoho-oauthtoken ${zohoAccessToken}`,
        },
      })
      .catch(async (error) => {
        functions.logger.error(error.response.data);
        throw new functions.https.HttpsError(
          'unknown',
          error.response.data.message,
          error.response.data
        );
      });
  });

更新:

我发现 this helpful thread 这表明有时无限循环不是来自 Cloud Function 而来自调用者,而且调用堆栈可以帮助调试。

出于兴趣,这是我的调用堆栈,如 Firebase 模拟器日志中所示。这对我来说没有意义,因为它只是一次又一次重复的同一行:

Unhandled error RangeError: Maximum call stack size exceeded

at TCP.get [as reading] (_tls_wrap.js:617:7)
at Function.entries (<anonymous>)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:157:37)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)
at encode (/Users/macbook/Dev/my-app/functions/node_modules/firebase-functions/lib/common/providers/https.js:158:22)

这里是 Chrome 浏览器控制台调用堆栈:

postJSON    @   index.esm2017.js?6f1f:499
call    @   index.esm2017.js?6f1f:553
await in call (async)       
eval    @   index.esm2017.js?6f1f:485
eval    @   SupportPage.vue?c3cc:37
eval    @   index.js??clonedRule…tup=true&lang=ts:15
__awaiter   @   index.js??clonedRule…tup=true&lang=ts:11
handleClick @   SupportPage.vue?c3cc:36
callWithErrorHandling   @   runtime-core.esm-bundler.js?f781:155
callWithAsyncErrorHandling  @   runtime-core.esm-bundler.js?f781:164
emit  @   runtime-core.esm-bundler.js?f781:718
eval    @   runtime-core.esm-bundler.js?f781:7232
onClick @   QBtn.js?9c40:148
callWithErrorHandling   @   runtime-core.esm-bundler.js?f781:155
callWithAsyncErrorHandling  @   runtime-core.esm-bundler.js?f781:164
invoker @   runtime-dom.esm-bundler.js?9cec:366

这是我的 Vue 应用程序中的调用函数:

<template>
  <v-btn design="alpha" @click="handleClick">loadData</v-btn>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import VBtn from 'src/components/VBtn.vue';
import { httpsCallable, FunctionsError } from 'firebase/functions';
import { functions } from 'src/config/firebase';

const data = ref();

const getZohoDeskTickets = httpsCallable(functions, 'getZohoDeskTickets');

const isFunctionsError = (error: unknown): error is FunctionsError => {
  return (error as FunctionsError).details !== undefined;
};

const handleClick = async () => {
  const ticket = await getZohoDeskTickets().catch((error) => {
    if (isFunctionsError(error)) {
      console.log(error.code);
      console.log(error.message);
      console.log(error.details);
    } else {
      console.log(error);
    }
  });
  data.value = ticket;
  console.log(ticket);
  return ticket;
};
</script>

但即便如此我还是想不通。

无限循环的原因是什么?

或者可能是其他原因导致了这个错误?

终于明白了!

解决方案来自

简而言之;我需要像这样在返回的 axios 链上添加一个 .then()

export const getZohoDeskTickets = functions
  .region('us-central1')
  .https.onCall(async (data, context) => {
    // Check if it passed App Check
    if (context.app == undefined) {
      throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called from an App Check verified app.'
      );
    }

    // Check the authentication
    if (!context.auth) {
      // Throwing an HttpsError so that the client gets the error details.
      throw new functions.https.HttpsError(
        'unauthenticated',
        'The function must be called while authenticated.'
      );
    }

    // Do the operation
    const zohoAccessToken = await getSecretVersion(
      'zoho-self-client-api-access-token'
    ).catch((error) => {
      throw new functions.https.HttpsError('unknown', error.message, error);
    });
    return axios
      .get('https://desk.zoho.com/api/v1/tickets', {
        headers: {
          orgId: '774638961',
          Authorization: `Zoho-oauthtoken ${zohoAccessToken}`,
        },
      })
      .then((response) => {
        functions.logger.info(response.data);
        return response.data;
      })
      .catch((error) => {
        functions.logger.error(error.response.data);
        throw new functions.https.HttpsError(
          'unknown',
          error.response.data.message,
          error.response.data
        );
      });
  });