[line-bot-sdk-nodejs] メッセージ送信に失敗した時、エラーメッセージの詳細はどこにあるのか

2022.10.21

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

line-bot-sdk-nodejs.pushMessage() などのリクエストに失敗した際、エラーの詳細はどこを見ればわかるのか一瞬迷ったので紹介します。

環境

  • node 16.13.0
  • typescript 4.7.4
  • @line/bot-sdk 7.5.2

エラーが発生するサンプルコード

TypeScriptを使っていきます。「TypeScriptならSDKの型定義に従って書けば実行時エラーは起きないのではないか?」と思いがちですが、意外にエラーは発生します。

サンプルコードを提示します。

import { Client } from "@line/bot-sdk"

const main = async () => {
  const client = new Client({
    channelSecret: "XXXXXXXXXXXXXXXX", // チャネルシークレットをセット
    channelAccessToken: "XXXXXXXXXXXXXXXXXXXXXX", // チャネルアクセストークンをセット
  })
  await client
    .pushMessage("Uxxxxxxxxxxxxxxxxxx", [ // 送信したいLINEユーザIDをセット
      {
        type: "text",
        text: "",
      },
    ])
    .catch((e: Error) => {
      console.error(
        JSON.stringify({
          message: e.message,
        })
      )
    })
}

main()

e.message だけを出力しています。

上記実行するとエラーとなり、以下が出力されます。

{"message":"Request failed with status code 400"}

これだけだと「ステータスコードが400」という情報しかわからず、コード修正の道筋を立てることが難しいです。

原因調査のために追加でどのようなプロパティを出力すると良いのでしょうか?

.originalError.response.* を出力するようにする

.originalError.response.data を出力すると詳細がわかります。

他にも .originalError.response.status .originalError.response.headers など出力できます。

import { Client } from "@line/bot-sdk"

const main = async () => {
  const client = new Client({
    channelSecret: "XXXXXXXXXXXXXXXX", // チャネルシークレットをセット
    channelAccessToken: "XXXXXXXXXXXXXXXXXXXXXX", // チャネルアクセストークンをセット
  })
  await client
    .pushMessage("Uxxxxxxxxxxxxxxxxxx", [ // 送信したいLINEユーザIDをセット
      {
        type: "text",
        text: "",
      },
    ])
    .catch((e: Error) => {
      if (e instanceof HTTPError) {
        console.error(
          JSON.stringify({
            message: e.message,
            status: e.originalError.response.status,
            data: e.originalError.response.data,
          })
        )
        return
      }

      console.error(
        JSON.stringify({
          message: e.message,
        })
      )
    })
}

main()

出力は以下です。

{"message":"Request failed with status code 400","status":400,"data":{"message":"The request body has 1 error(s)","details":[{"message":"May not be empty","property":"messages[0].text"}]}}

引数の text が空文字列 "" であることが原因とわかりました。これはSDKの型定義による静的解析ではチェックされない点になります。

修正方針としては1文字以上の文字列を渡せば良いということになります。

調べた道筋

上記はline-bot-sdk-nodejsのREADMEなどには載っていなさそうです。

どのようにすれば e.originalError.response.* の存在を知ることができそうでしょうか?備忘録兼ねまして紹介したいと思います。

ErrorオブジェクトをDumpして調べる

まず思いつくのがErrorオブジェクト全体をDumpして調べる方法です。

.catch((e: Error) => {
+ console.dir(e, { depth: null })

こちらの出力結果(割愛します)から、 e.originalError.response.data に詳しいエラー理由が入っていると判断できます。

ソースコード+ドキュメントから調べる

line-bot-sdk-nodejsのソースコードからも追ってみます。

リクエスト失敗時は HTTPError というエラークラスを投げているようなので、こちらの定義を見ます。

https://github.com/line/line-bot-sdk-nodejs/blob/2dbc6f688dce465ad8b181a8eb575155bad29dd4/lib/exceptions.ts#L34

HTTPError のコンストラクタに4つの引数を渡しており、引数名からこの中で詳細なエラーが得られそうなものは originalError と当たりをつけます。

そして new HTTPError している箇所をソース内検索で探します。

https://github.com/line/line-bot-sdk-nodejs/blob/2dbc6f688dce465ad8b181a8eb575155bad29dd4/lib/http.ts#L140-L146

こちらより AxiosErrororiginalError としてセットしていることがわかります。line-bot-sdk-nodejsはAxiosに依存しており、ライブラリ利用者向けにもaxiosのエラーをそのまま扱っていそうだということがわかりました。

ということでaxiosのドキュメントを見ます。

https://github.com/axios/axios#handling-errors

      console.log(error.response.data);
      console.log(error.response.status);
      console.log(error.response.headers);

のサンプルコードより、詳細(HTTPレスポンスボディ)を得るには .originalError.response.data を取得すれば良さそうと推測でき、調査完了となります。

ちょっと予防線ですが、全体を丁寧に読み込めてはいないのでもし間違いがあればご指摘ください。

まとめ

line-bot-sdk-nodejsのエラーログ出力時には .originalError.response.data を含めることで原因調査に役立つことを紹介しました。

そもそもErrorオブジェクト全体を出力するという手もありそうです。ただ実案件においては、

  • 冗長なエラーログを避けたい
  • ログ出力に機密情報が含まれることを避けたい

などの事情で、ログ出力対象を常に指定する意思決定もあるかと思います。

以上参考になれば幸いです。

参考