LINE Messaging API “Cannot read property ‘text’ of undefined” エラーが発生した時の対応メモ

技術メモ

LINE Messaging APIを使用して、LINEボットを開発していました。

そこでメッセージイベントに加えてポストバックイベントのイベント処理を実装する必要がありました。

とりあえずメッセージイベント発生時はLINEに”This is a test”を送信し、ポストバックイベント発生時はポストバックの内容(postback.data)をログに出力するよう実装しました。

以下、Lambda(Node.js)のソースコードです。

'use strict';
const line = require('@line/bot-sdk');
const crypto = require('crypto');
const client = new line.Client({ channelAccessToken: process.env.ACCESSTOKEN });

function executeLambdaResponse(context) {
    let lambdaResponse = {
        statusCode: 200,
        headers: { "x-line-status": "OK" },
        body: '{"result":"connect check"}'
    };
    context.succeed(lambdaResponse);
}

exports.handler = (event, context) => {

    let signature = crypto.createHmac('sha256', process.env.CHANNELSECRET).update(event.body).digest('base64');
    let checkHeader = (event.headers || {})['x-line-signature'];

    let body = JSON.parse(event.body);
    let text = body.events[0].message.text
    let postback = body.events[0].postback.data

    if (signature === checkHeader) {
        if (body.events[0].replyToken === '00000000000000000000000000000000') { //接続確認エラー回避
            executeLambdaResponse(context)
        } else if (text == "test") {
            const message = {
                'type': 'text',
                'text': 'This is a test'
            };
            client.replyMessage(body.events[0].replyToken, message)
                .then(() => {
                    executeLambdaResponse(context)
                }).catch((err) => console.log(err));
        } else if (postback == "test2") {
            console.log(postback)
        }
    } else {
        console.log('署名認証エラー');
    }
    console.log(`EVENT: ${JSON.stringify(event)}`);

};

そして、実際にLINEからポストバックイベントを発生させるとLambdaで以下のエラーが発生しました。

{
    "errorType": "TypeError",
    "errorMessage": "Cannot read property 'text' of undefined",
    "stack": [
        "TypeError: Cannot read property 'text' of undefined",
        "    at Runtime.exports.handler (/var/task/index.js:128:37)",
        "    at Runtime.handleOnce (/var/runtime/Runtime.js:66:25)"
    ]
}

textが定義されていないとのことです。以下の記載に問題があることがわかりました。

let text = body.events[0].message.text
let postback = body.events[0].postback.data

ポストバックイベント発生時に生成されるオブジェクトにはmessageプロパティが存在しません。そのためbody.events[0].message.textが参照不可となりtext undefinedのエラーになったみたいです。

イベントの種類によってLINE側で生成するオブジェクトの構成が変わるのでそれを考慮して実装する必要がありました。

下のように存在しないプロパティの参照を防ぐよう修正したら、エラーを解決できました。

   let text = ""
    let postback = ""
    if (body.events[0].type == "message") {
        text = body.events[0].message.text
    } else if (body.events[0].type == "postback") {
        postback = body.events[0].postback.data
    }

メッセージイベント発生時にはbody.events[0].message.textプロパティにアクセスし、ポストバックイベント発生時にはbody.events[0].postback.dataプロパティにアクセスするようにしています。

修正後のソースコード(全体)

'use strict';
const line = require('@line/bot-sdk');
const crypto = require('crypto');
const client = new line.Client({ channelAccessToken: process.env.ACCESSTOKEN });

function executeLambdaResponse(context) {
    let lambdaResponse = {
        statusCode: 200,
        headers: { "x-line-status": "OK" },
        body: '{"result":"connect check"}'
    };
    context.succeed(lambdaResponse);
}

exports.handler = (event, context) => {

    let signature = crypto.createHmac('sha256', process.env.CHANNELSECRET).update(event.body).digest('base64');
    let checkHeader = (event.headers || {})['x-line-signature'];

    let body = JSON.parse(event.body);
    let text = ""
    let postback = ""
    if (body.events[0].type == "message") {
        text = body.events[0].message.text
    } else if (body.events[0].type == "postback") {
        postback = body.events[0].postback.data
    }

    if (signature === checkHeader) {
        if (body.events[0].replyToken === '00000000000000000000000000000000') { //接続確認エラー回避
            executeLambdaResponse(context)
        } else if (text == "test") {
            const message = {
                'type': 'text',
                'text': 'This is a test'
            };
            client.replyMessage(body.events[0].replyToken, message)
                .then(() => {
                    executeLambdaResponse(context)
                }).catch((err) => console.log(err));
        } else if (postback == "test2") {
            console.log(postback)
        }
    } else {
        console.log('署名認証エラー');
    }
    console.log(`EVENT: ${JSON.stringify(event)}`);

};

あとがき

複数のtypeのイベントオブジェクトを扱う予定がある人は注意ですね。それぞれのtypeが持つプロパティの情報は以下の公式ドキュメントから確認できます。

コメント

タイトルとURLをコピーしました