kokogento
@kokogento (ここ げんと)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

cloud functionsのエラー「Error: Process exited with code 16」が発生する原因は?

cloud functions謎のエラーの原因は?

Error: Process exited with code 16
    at process.on.code (/layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/invoker.js:275:22)
    at process.emit (events.js:198:13)
    at process.EventEmitter.emit (domain.js:448:20)
    at process.exit (internal/process/per_thread.js:168:15)
    at Object.sendCrashResponse (/layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/logger.js:37:9)
    at process.on.err (/layers/google.nodejs.functions-framework/functions-framework/node_modules/@google-cloud/functions-framework/build/src/invoker.js:271:22)
    at process.emit (events.js:198:13)
    at process.EventEmitter.emit (domain.js:448:20)
    at emitPromiseRejectionWarnings (internal/process/promises.js:140:18)
    at process._tickCallback (internal/process/next_tick.js:69:34) 

cloud functionsでmonthCountEvents関数をデプロイすると、monthCountEventsが発動するたびにこのようなエラーが発生します。

スクリーンショット 2021-02-01 19.39.46.jpg

このエラーの原因は何でしょうか?

何を実現したいか?

Firebase 要素の数をカウントするなら、最初からこの方法でいきましょう!(Cloud Functions)
:point_up:上記の記事を参考に、Firestoreのドキュメントの数をカウントしています。

users/{userId}/events/{eventId}のドキュメントに変化があった場合、Firestore内のドキュメントフィールド2箇所にカウントを記録します。

exports.monthCountEvents = functions.region('asia-northeast1').firestore
  .document('users/{userId}/events/{eventId}')
  .onWrite((change, context) => {

該当のコード

ほぼ同じようなコードでも、エラーが「発生しないパターン」「発生するパターン」があります。。。:worried:

エラーが発生しないパターン

functions/index.ts
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.firestore();

// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript

// eventが作成された時にカウントする
exports.countEvents = functions.region('asia-northeast1').firestore
  .document('users/{userId}/events/{eventId}')
  .onWrite((change, context) => {

    let periods: boolean;
    let dayEventCounts: number;
    if (!change.before.exists) {
      periods = change.after.get('periods');
      dayEventCounts = change.after.get('dayEventCounts');
    } else {
      periods = change.before.get('periods');
      dayEventCounts = change.before.get('dayEventCounts');
    }

    const userId = context.params.userId;
    const FieldValue = admin.firestore.FieldValue;
    const countsRef = db.collection('users').doc(userId);    

    if (!change.before.exists && !periods) {
      // 登録時に件数をインクリメント
      countsRef.update({ eventCount: FieldValue.increment(dayEventCounts) });
      return
    } else if (change.before.exists && !change.after.exists && !periods) {
      // 削除時に件数をデクリメント
      countsRef.update({ eventCount: FieldValue.increment(-dayEventCounts) });
      return
    }
    return;
  });

exports.monthCountEvents = functions.firestore.document('users/RmSJbxK2A2QNWZbm2nLQwEr7tNw2/events/{eventId}')
  .onWrite((change) => {
    let pushMonths: any;
    let periods: boolean;
    let dayEventCounts: number;
    if (!change.before.exists) {
      pushMonths = change.after.get('pushMonths');
      periods = change.after.get('periods');
      dayEventCounts = change.after.get('dayEventCounts');
    } else {
      pushMonths = change.before.get('pushMonths');
      periods = change.before.get('periods');
      dayEventCounts = change.before.get('dayEventCounts');
    }

    const FieldValue = admin.firestore.FieldValue;
    const countsRef = db.collection(`users/RmSJbxK2A2QNWZbm2nLQwEr7tNw2/counts`).doc(pushMonths);
    countsRef.get().then((docSnapshot) => {
      if (!docSnapshot.exists) {
        countsRef.set({ monthCount: 1, monthDate: pushMonths });
      } 
    });

     if (!change.before.exists && !periods) {
      // 登録時に件数をインクリメント
      return countsRef.update({ monthCount: FieldValue.increment(dayEventCounts) } );
    } else if (change.before.exists && !change.after.exists && !periods) {
      // 削除時に件数をデクリメント
      return countsRef.update({ monthCount: FieldValue.increment(-dayEventCounts) });
     }
    return
  })

このmonthCountEventsはなんのエラーもなく発動します。
これはテスト用なので、自分のuidを指定してonWriteがトリガーするようにしています

エラーが発生するパターン

functions/index.ts
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.firestore();

// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript

// eventが作成された時にカウントする
exports.countEvents = functions.region('asia-northeast1').firestore
  .document('users/{userId}/events/{eventId}')
  .onWrite((change, context) => {

    let periods: boolean;
    let dayEventCounts: number;
    if (!change.before.exists) {
      periods = change.after.get('periods');
      dayEventCounts = change.after.get('dayEventCounts');
    } else {
      periods = change.before.get('periods');
      dayEventCounts = change.before.get('dayEventCounts');
    }

    const userId = context.params.userId;
    const FieldValue = admin.firestore.FieldValue;
    const countsRef = db.collection('users').doc(userId);    

    if (!change.before.exists && !periods) {
      // 登録時に件数をインクリメント
      countsRef.update({ eventCount: FieldValue.increment(dayEventCounts) });
      return
    } else if (change.before.exists && !change.after.exists && !periods) {
      // 削除時に件数をデクリメント
      countsRef.update({ eventCount: FieldValue.increment(-dayEventCounts) });
      return
    }
    return;
  });

exports.monthCountEvents = functions.region('asia-northeast1').firestore
  .document('users/{userId}/events/{eventId}')
  .onWrite((change, context) => {

    let pushMonths: any;
    let periods: boolean;
    let dayEventCounts: number;
    if (!change.before.exists) {
      pushMonths = change.after.get('pushMonths');
      periods = change.after.get('periods');
      dayEventCounts = change.after.get('dayEventCounts');
    } else {
      pushMonths = change.before.get('pushMonths');
      periods = change.before.get('periods');
      dayEventCounts = change.before.get('dayEventCounts');
    }

    const userId = context.params.userId;
    const FieldValue = admin.firestore.FieldValue;
    const monthCountRef = db.collection(`users/${userId}/counts`).doc(pushMonths);
    monthCountRef.get().then((docSnapshot) => {
      if (!docSnapshot.exists) {
        monthCountRef.set({ monthCount: 1, monthDate: pushMonths });
      }
    });

    if (!change.before.exists && !periods) {
      // 登録時に件数をインクリメント
      return monthCountRef.update({ monthCount: FieldValue.increment(dayEventCounts) });
    } else if (change.before.exists && !change.after.exists && !periods) {
      // 削除時に件数をデクリメント
      return monthCountRef.update({ monthCount: FieldValue.increment(-dayEventCounts) });
    }
    return;
  });

このmonthCountEventsは本番用なので、自分のuidではなくワイルドカードを使い、onWriteをトリガーしています。

何が違って、何が原因なのかさっぱりわかりません。。。
どなたかご教授ください!!

0

2Answer

おそらくですが、エラーのバックトレースに emitPromiseRejectionWarnings とあるので、 .catch() していない Promise が失敗したときに出るエラーだと思います。 Node.js でそのような失敗する Promise を実行すると似たエラーが出ます:

$ node
Welcome to Node.js v14.15.1.
Type ".help" for more information.
> new Promise(() => { throw "x" }).then(() => {})
Promise { <pending> }
> (node:6473) UnhandledPromiseRejectionWarning: x
(Use `node --trace-warnings ...` to show where the warning was created)
(node:6473) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:6473) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

ご質問のエラーが発生するパターンでは monthCountRef.get() が返す Promise の .catch() を呼んでいません。

    monthCountRef.get().then((docSnapshot) => {
      if (!docSnapshot.exists) {
        monthCountRef.set({ monthCount: 1, monthDate: pushMonths });
      }
    });

以下のようにPromise の失敗理由をログに出す .catch() を追加して理由を調べてみてください。

    monthCountRef.get().then((docSnapshot) => {
      if (!docSnapshot.exists) {
        monthCountRef.set({ monthCount: 1, monthDate: pushMonths });
      }
    }).catch((error) => {
      console.log("Error getting document:", error);
    });

エラーが発生しないパターンの countsRef.get() が返す Promise も同様です。こちらは成功しているためにエラーになっていないようですが、きちんと .catch() してください。

3Like

Comments

  1. @kokogento

    Questioner

    ありがとうございます〜!!
    catchを使うと、確かに謎のエラーは出なくなりました!

.catch()を使うことで謎のエラーは回避できるようになりました。
@uasi さん、ご教授ありがとうございました:grin:

どうやら僕は、Node.jsをあまりよくわからないままcloud functionsを使っていたようです。。。

0Like

Your answer might help someone💌