【小ネタ】Dagger(Node.js SDK)で「failed to decode *core.directoryIDPayload bytes」のエラーを出した時の解決方法

failed to decode core.directoryIDPayload bytes: illegal base64 data at input byte 15というエラーをDaggerのNode.js SDKで遊んでいる際に出してしまいました。原因は私の誤認です。VSCodeでも使える定義ジャンプでソースコードをみることであっさり解決できました。今のエディタって本当に便利ですね。
2023.01.03

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

こんにちは。AWS事業本部コンサルティング部に所属している今泉(@bun76235104)です。

まだ熟成していない技術を触るのって楽しい反面、ドキュメントが少なくて苦労する時ってありますよね?

私は先日DaggerというCI/CDに役立つツールがNode.js SDKをリリースしていたことに気づき、試してみました!

Daggerというサービス自体がどんなものか気になる方は、こちらの記事をご参照ください。

その後、「ちょっと実践的な使い方もしてみたいな」ということで、既存のDockerfileからコンテナイメージをビルドしてECRにプッシュしてみることにしました。

現時点でバージョンがv0.3.0(2022年1月1日執筆時点)ということもあり、まだまだ発展途上です。

何点か詰まった点があるため、記事を書いてみることにしました。

今回は既存のDockerfileからコンテナイメージをビルドするまでに詰まった点について記載します。

具体的には以下のエラーの解消方法と調査方法についてです。

[
	{
		"message": "failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15",
		"locations": [
			{
				"line": 5,
				"column": 9
			}
		],
		"path": [
			"container",
			"build"
		]
	}
]

先に結論(エラーの原因と解決方法)

以下のようなディレクトリ構造で、Dockerfileからイメージをビルドしようとしていました。

.
├── app
│   ├── Dockerfile #このDockerfileを利用したい
│   ├── app.py
│   └── requirements.txt
├── cicd.ts
├── package-lock.json
├── package.json
├── sample_env
└── tsconfig.json

cicd.ts

import Client, { connect } from "@dagger.io/dagger";
import * as path from "path";

// initialize Dagger client
connect(async (client: Client) => {
  try {
    // appディレクトリ配下のDockerfileからビルド
    const builder = client.container().build(path.join(process.cwd(), "app"));
    // ここでエラーが発生する
    const id = await builder.id();
    console.log(id);
  } catch (e) {
    console.log("Error!!");
    const json = JSON.stringify(e);
    console.log(json);
  }
});

原因は上記ハイライト部分です。

以下画像は私が利用するエディタの型情報のヒントです。

引数として渡す contextという変数名から「これはDockerのビルドコンテキストのことだな。今回app直下でDockerfileからビルドしたいからパスを渡そう」と誤認してしまいました。

20230101_dagger_nodejs_build_error_hovber_editor

型として文字列を受け取れるため、コンパイル時点ではこちらが間違いと気づかなかったというわけです。

実際のソースコードを見ればわかるのですが、ここで文字列を渡す場合はDirectoryIDを引き渡す必要があり、パスを表す文字列ではありません。

build(
    context: DirectoryID | Directory,
    opts?: ContainerBuildOpts
  ): Container {
    return new Container({
      queryTree: [
        ...this._queryTree,
        {
          operation: "build",
          args: { context, ...opts },
        },
      ],
      host: this.clientHost,
      sessionToken: this.sessionToken,
    })
  }
  // 略

ということで以下のように、Daggerで定義されているDirectoryクラスのインスタンスを利用することで無事解決しました。

connect(async (client: Client) => {
  try {
    const buildDir = client.host().directory(path.join(process.cwd(), "app"));
    const builder = client.container().build(buildDir);
    await builder.publish(
      `${process.env.PUBLISH_TARGET}:${process.env.PUBLISH_TAG}`
    );
  } catch (e) {
    console.log(JSON.stringify(e));
    process.exit(1);
  }
});

おまけ:調査する時のTips

エラーメッセージからヒントを探す

まだ以下のコードでエラーを出してしまった時です。

cicd.ts

import Client, { connect } from "@dagger.io/dagger";
import * as path from "path";

// initialize Dagger client
connect(async (client: Client) => {
  try {
    // appディレクトリ配下のDockerfileからビルド
    const builder = client.container().build(path.join(process.cwd(), "app"));
    // ここでエラーが発生する
    const id = await builder.id();
    console.log(id);
  } catch (e) {
    console.log("Error!!");
    const json = JSON.stringify(e);
    console.log(json);
  }
});

この時はエラーメッセージのハンドリングもしておらず、以下のようにちょっと見にくい形で表示されていました。

 cause: ClientError: failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15: {"response":{"data":null,"errors":[{"message":"failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15","locations":[{"line":5,"column":9}],"path":["container","build"]}],"status":200,"headers":{}},"request":{"query":"\n        {\n        container  {\n      \n        build (context: \"/path-to-app/dagger_nodejs/ecr_python/app\") {\n      \n        id  }}\n      }\n      "}}

ちょっと嫌になったので\nとなっている箇所を取り除き、以下のように見やすい形に整形してみました。

[
	{
		"message": "failed to decode *core.directoryIDPayload bytes: illegal base64 data at input byte 15",
		"locations": [
			{
				"line": 5,
				"column": 9
			}
		],
		"path": [
			"container",
			"build"
		]
	}
]

この時点ではDirectoryIDというものを認識していないので、*core.directoryIDPayload bytesというメッセージをみてもさっぱりわかりませんが、path: ['container', 'build'] というメッセージから、ビルドが原因であろうことはわかりました。

そこでエディタの定義ジャンプ機能を使って、ソースコードを確認したところ上記のようなミスに気づいたという経緯です。

例えばVSCodeの場合、調べたいメソッドの上でF12を押下することでジャンプできます。(参考: 【Windows版】VS Code キーボードショートカット一覧 (オススメ付き) - Qiita

20230101_dagger_nodejs_teigi_jump1

この状態でF12を押下することで、以下のようにソースコードにジャンプできます。

20230101_dagger_nodejs_teigi_jump2

「エラーをよくみる」「ソースコードを見る」という基本をおざなりにしたらダメだなと思いました(子どものような感想)。