"The XML you provided was not well-formed or did not validate against our published schema" と言われたら……
Node.js の AWS SDK で、S3のコマンド実行時に
MalformedXML: The XML you provided was not well-formed or did not validate against our published schema
と言われたら、 S3コマンドオブジェクトに余計なプロパティを渡してしまっていないかチェックしましょう!
典型的な例として、ListObjectsV2Command
の返り値である _Object[]
(S3 Getで得られる色々な情報が入ったオブジェクト)をキャストしてそのまま渡しているケースが考えられます。
import {
type _Object,
type ObjectIdentifier,
type S3Client,
DeleteObjectsCommand,
ListObjectsV2Command,
} from "@aws-sdk/client-s3"; // バージョン '3.887.0' で検証
const deleteAllObjects = async (s3Client: S3Client, bucketName: string, prefix: string) => {
// 削除対象のファイルの取得
// 一気に取得できない場合の継続トークンの処理などは省略
const listCommand = new ListObjectsV2Command({
Bucket: bucketName,
Prefix: prefix,
});
const listResponse = await s3Client.send(listCommand);
const filesToDelete: _Object[] = listResponse.Contents ?? [];
// 削除対象のファイルの削除
const deleteCommand = new DeleteObjectsCommand({
Bucket: bucketName,
Delete: {
// 🙅 NG: { Key: string } を含む _Object[] 型をそのまま渡しているが、これをやるとリクエストエラーになる
Objects: filesToDelete.Contents as { Key: string }[],
// 👍️ OK: 正しくは以下のように Key プロパティを抽出して渡す (ObjectIdentifier[] 型に合わせる)
Objects: filesToDelete.map(obj => ({ Key: obj.Key! })),
},
})
await s3Client.send(deleteCommand);
}
上記は互換サーバ (MinIO) ではエラーになりませんでした。いきなり結合や本番で遭遇するかもしれませんので、気をつけましょう。
具体的に何が問題なのか深堀りしてみる
ListObjectsV2Command
の返り値 _Object[]
には以下のようなプロパティが含まれています。 Key
の他に色々含まれていることが分かります。
interface _Object {
Key?: string;
LastModified?: Date;
ETag?: string;
Size?: number;
StorageClass?: string;
Owner?: {
DisplayName?: string;
ID?: string;
};
}
一方、DeleteObjectsCommand
の Objects
に期待される型は以下だけです。通常は Key
プロパティのみを指定するでしょう。
interface ObjectIdentifier {
Key: string;
VersionId?: string;
}
では、 ListObjectsV2Command
で取得された _Object
を渡すと実際にどのようなリクエスト電文を送っているか、デバッグ実行で実際に確認してみましょう。@smithy/middleware-serde@4.0.8
の src/serializerMiddleware.ts
のコメントあたりをチェックすれば確認できます。
<?xml version="1.0" encoding="UTF-8"?>
<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Object>
<Key>test/foo/bar/filesToBeDeleted.msu8</Key>
<ETag>"9473fdd0d880a43c21b7778d34872157"</ETag>
<Size>12</Size>
</Object>
<Quiet>false</Quiet>
</Delete>
<!--
🙅 エラー: The XML you provided was not well-formed or did not validate against our published schema
-->
この記事に書いてあるとおりにエラーとなりました。ちなみに余計なプロパティをコマンドに付けて送ろうとしてもXMLへのシリアライゼーション過程で省かれるようです。シリアライザがキーを固定して値を拾っているようです。
次に正常なリクエストを送るために、Key
プロパティだけを抽出して渡した場合のリクエスト電文を確認してみます。
<?xml version="1.0" encoding="UTF-8"?>
<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Object>
<Key>test/foo/bar/filesToBeDeleted.msu8</Key>
</Object>
<Quiet>false</Quiet>
</Delete>
<!--
👍️ OK: 正常に削除される
-->
これは成功しました。それなら、ETag
か Size
のどちらかがエラーの原因なのか?
<?xml version="1.0" encoding="UTF-8"?>
<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Object>
<Key>test/foo/bar/filesToBeDeleted.msu8</Key>
<ETag>"9473fdd0d880a43c21b7778d34872157"</ETag>
</Object>
<Quiet>false</Quiet>
</Delete>
<!--
👍️ OK: 正常に削除される
-->
ETag
は問題ないようです。
では次に Size
を渡してみます。
<?xml version="1.0" encoding="UTF-8"?>
<Delete xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Object>
<Key>test/foo/bar/filesToBeDeleted.msu8</Key>
<Size>12</Size>
</Object>
<Quiet>false</Quiet>
</Delete>
<!--
🙅 エラー: The XML you provided was not well-formed or did not validate against our published schem
-->
そういうことで Sizeプロパティを渡すとリクエストが失敗する ようでした。ええ〜!?1
- 実験リージョン:
ap-northeast-1
- AWS SDK:
@aws-sdk/client-s3@3.887.0
-
もちろんAWS SDKのXMLシリアライザの出力と、AWSサービス側のXMLスキーマが異なっているように見えることについて心外であるという意味合いです ↩