AWS Emulatorの501エラーを見分けて対応状況を調べる方法
AWS Emulator は、まだ作られていない操作を呼ばれると、HTTP 501 とエラーコード NotImplemented を返します。ローカルの結合テストや CI で AWS の API を呼ぶテストが落ちたとき、このエラーを見分けられれば、自分のコードの誤りを探す手間を省けます。あわせて、リリースごとに公開される fidelity のスコアのファイルを読み、使う操作が AWS と同じに動くかを調べる手順も載せます。
検証環境は Ubuntu 24.04(x86_64)、AWS Emulator 1.0.352(Debian パッケージ)、AWS CLI 2.40.3、Python 3.11 と boto3 1.44.3 です。AWS のアカウントは使わず、リクエストはすべて手元の AWS Emulator(127.0.0.1:9297)に送りました。本物の AWS には接続していないので、本物の AWS での動きは、公式ドキュメントと、AWS が公開しているスコアのファイルの記録をもとに書いています。評価スイートのランナー(fidelity_tests_runner)での実行、Docker での起動、Python 以外の SDK は試していません。
対応していない操作を呼んだときのエラー
AWS Emulator は、公式ガイドにある Linux での入れ方のとおりに Debian パッケージで入れました。1つ目のターミナルで起動します。-t off は、使用状況のデータ(テレメトリー)を送らない指定です。AWS Emulator は、止めるまでこのターミナルで動き続けます。
aws-emulator -t off
2つ目のターミナルで、公式ガイドにある、SDK の接続先を AWS Emulator に向ける手順のとおりに環境変数を設定しました。最後の AWS_PAGER は、AWS CLI の出力をページャーで開かずにそのまま表示するための指定で、検証のために足したものです。
export AWS_ENDPOINT_URL=https://127.0.0.1:9297
export AWS_SHARED_CREDENTIALS_FILE=~/.aws/emulator/9297/emulator_credentials
export AWS_CONFIG_FILE=~/.aws/emulator/9297/config
export AWS_CA_BUNDLE=~/.aws/emulator/9297/ca.pem
export AWS_EC2_METADATA_DISABLED=true
export AWS_DISABLE_HOST_PREFIX_INJECTION=true
export AWS_REGION=us-east-1
export AWS_DEFAULT_REGION=us-east-1
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN AWS_PROFILE
export AWS_PAGER=""
この状態で、Systems Manager(SSM)の OpsCenter の項目を検索する DescribeOpsItems を呼ぶと、次のエラーが出ました。
$ aws ssm describe-ops-items
aws: [ERROR]: An error occurred (NotImplemented) when calling the DescribeOpsItems operation: operation DescribeOpsItems is scaffolded but not implemented in this emulator
--debug を付けて HTTP の応答を見ると、ステータスは 501 でした。
$ aws ssm describe-ops-items --debug 2>&1 | grep -E 'HTTP/1.1" [0-9]+|^b'
2026-11-14 02:22:16,178 - MainThread - urllib3.connectionpool - DEBUG - https://127.0.0.1:9297 "POST / HTTP/1.1" 501 117
b'{"__type":"NotImplemented","message":"operation DescribeOpsItems is scaffolded but not implemented in this emulator"}'
botocore.exceptions.ClientError: An error occurred (NotImplemented) when calling the DescribeOpsItems operation: operation DescribeOpsItems is scaffolded but not implemented in this emulator
公式ガイドの fidelity の説明にも、まだ作られていない操作には HTTP 501 とエラーコード NotImplemented を返す、と書かれています。CloudWatch Logs の TestTransformer でも、同じ形のメッセージが返りました。
$ aws logs test-transformer --transformer-config '[{"parseJSON":{}}]' --log-event-messages '{"a":1}'
aws: [ERROR]: An error occurred (NotImplemented) when calling the TestTransformer operation: operation TestTransformer is scaffolded but not implemented in this emulator
AWS CLI は 501 の応答を再試行しませんでした。--debug の出力で 501 の応答を数えると、1 回だけでした。
対応している操作のエラーとの違い
比べるために、AWS Emulator が対応している SSM の Parameter Store を使いました。値の保存と読み出しは成功します。
$ aws ssm put-parameter --name /demo/greeting --value hello --type String
{
"Version": 1,
"Tier": "Standard"
}
$ aws ssm get-parameter --name /demo/greeting --query Parameter.Value --output text
hello
存在しない名前を読みにいくと、ParameterNotFound のエラーになりました。ステータスは 400 です。公式ガイドの fidelity の説明によると、AWS Emulator は、作られている操作では正しくない呼び出しに AWS と同じエラーを返すことを目指しています。
$ aws ssm get-parameter --name /demo/missing
aws: [ERROR]: An error occurred (ParameterNotFound) when calling the GetParameter operation:
$ aws ssm get-parameter --name /demo/missing --debug 2>&1 | grep -E 'HTTP/1.1" [0-9]+|^b'
2026-11-14 02:22:15,433 - MainThread - urllib3.connectionpool - DEBUG - https://127.0.0.1:9297 "POST / HTTP/1.1" 400 30
b'{"__type":"ParameterNotFound"}'
b'{"__type":"ParameterNotFound"}'
botocore.errorfactory.ParameterNotFound: An error occurred (ParameterNotFound) when calling the GetParameter operation:
違いをまとめると、次のようになります。
| 呼んだ操作 | HTTP ステータス | エラーコード | メッセージ | AWS CLI の終了コード |
|---|---|---|---|---|
GetParameter(存在しない名前) |
400 | ParameterNotFound |
なし | 254 |
DescribeOpsItems(未対応) |
501 | NotImplemented |
… is scaffolded but not implemented in this emulator |
254 |
AWS CLI の終了コードは、どちらも 254 でした。AWS CLI の終了コードの説明では、254 は「リクエストを送り、サービスがエラーを返した」ことを表します。終了コードでは区別できないので、シェルのスクリプトではエラーの文面を見ます。
S3 の RenameObject は、AWS でも 501 になると記録されている
NotImplemented が出ても、AWS Emulator の未対応とは限りません。Amazon S3 の RenameObject を、汎用バケット(ふつうの S3 バケット)に対して呼ぶと、メッセージが違いました。
$ aws s3api rename-object --bucket demo-bucket-501 --key b.txt --rename-source a.txt
aws: [ERROR]: An error occurred (NotImplemented) when calling the RenameObject operation: A query parameter you provided implies functionality that is not implemented
同じコマンドに --debug を付けると、ステータスは 501 で、本文は S3 の形式の XML でした(RequestId と HostId は省略しました)。
<Error><Code>NotImplemented</Code><Message>A query parameter you provided implies functionality that is not implemented</Message><QueryParameter>renameObject</QueryParameter>…</Error>
S3 の RenameObject の API リファレンスによると、この操作を使えるのは S3 Express One Zone のディレクトリバケットだけです。S3 のエラーコードの一覧にも、NotImplemented(501 Not Implemented)が S3 自身のエラーとして載っています。さらに、後で読むスコアのファイルには、汎用バケットでの RenameObject について、AWS でも 501 の NotImplemented が返ったと読める記録がありました。
NotImplemented が返ったら、メッセージを読みます。… is scaffolded but not implemented in this emulator なら、AWS Emulator の未対応です。それ以外の文面なら、本物の AWS でも同じエラーになる場合があるので、サービスのドキュメントやスコアのファイルで確かめます。
テストのコードで見分ける(Python・boto3)
boto3 では、エラーを ClientError として受け取れます。HTTP ステータス、エラーコード、メッセージの3つを見る関数を書き、ここまでの3つの操作で試しました。
import boto3
from botocore.exceptions import ClientError
def not_implemented_in_emulator(err: ClientError) -> bool:
status = err.response["ResponseMetadata"]["HTTPStatusCode"]
code = err.response["Error"]["Code"]
message = err.response["Error"].get("Message", "")
return status == 501 and code == "NotImplemented" and "not implemented in this emulator" in message
ssm = boto3.client("ssm")
s3 = boto3.client("s3")
calls = {
"ssm.get_parameter": lambda: ssm.get_parameter(Name="/demo/missing"),
"ssm.describe_ops_items": lambda: ssm.describe_ops_items(),
"s3.rename_object": lambda: s3.rename_object(Bucket="demo-bucket-501", Key="b.txt", RenameSource="a.txt"),
}
for name, call in calls.items():
try:
call()
print(name, "ok")
except ClientError as err:
print(name,
err.response["ResponseMetadata"]["HTTPStatusCode"],
err.response["Error"]["Code"],
"emulator-gap" if not_implemented_in_emulator(err) else "service-error")
ssm.get_parameter 400 ParameterNotFound service-error
ssm.describe_ops_items 501 NotImplemented emulator-gap
s3.rename_object 501 NotImplemented service-error
未対応と判定されたのは DescribeOpsItems だけで、S3 の RenameObject は 501 でも未対応とは判定されませんでした。
なお、公式ガイドの版と互換性の説明には、同じメジャーバージョンの中で変わらないものの一覧がありますが、エラーのメッセージはその中に入っていません。版を上げたら、メッセージの文面が変わっていないか確かめてください。
fidelity のスコアのファイルで対応状況を調べる
ある操作が AWS と同じに動くかは、AWS が公開している fidelity のスコアで調べられます。公式ガイドの fidelity の説明によると、fidelity テストは、AWS SDK で一連の操作を実行し、AWS での結果やエラーと同じになるかを確かめる短いプログラムです。サービスごとのスコアは「通った fidelity テストの数/そのサービスの fidelity テストの数」で、リリースのたびに AWS Emulator のリリースのページで公開されます。公式ガイドには、スコアを比べるのは同じ版の fidelity テストどうしに限るよう書かれています。
スコアのファイルとテストの一覧を取得する
まず、使っている版とコミットを確かめます。
$ aws-emulator -v
Version: 1.0.352
Release date: 2026.11.13
Commit: c479b9c7e416e3201fdeba8fafd9c65b0eed8c74
v1.0.352 のリリースには、scores.json.gz というファイルが付いていました。これと、テストの一覧(manifest.jsonl)がある評価スイートのリポジトリを、同じフォルダに取得します。
curl -fsSLO https://github.com/aws/aws-emulator/releases/download/v1.0.352/scores.json.gz
git clone -q --depth 1 https://github.com/aws/aws-emulator-evaluation-suite.git
manifest.jsonl は1行が1つのテストで、テストが呼ぶ操作が operations に、テストの種類が labels に入っています(次の例は operations の一部を省略)。
{"check_id": "accessanalyzer.access-preview-detects-public-s3-bucket-policy", "deployments": [], "key": "accessanalyzer/access-preview-detects-public-s3-bucket-policy", "labels": ["axis:happy-path", "plane:control"], "operations": ["accessanalyzer:CreateAccessPreview", "accessanalyzer:CreateAnalyzer", …], "path": "behavioral/accessanalyzer/check_access_preview_detects_public_s3_bucket_policy.py", "service": "accessanalyzer"}
スコアのファイルを読むスクリプト
scores.json.gz の形式を説明した文書は、公式ガイドにも評価スイートの README にも見つかりませんでした。そこで、中身を見ながら次のスクリプトで数えました。項目の意味は、項目名と中身から私が読み取ったものです。
import gzip, json, collections
scores = json.load(gzip.open("scores.json.gz"))
units = scores["units"]
manifest = [json.loads(l) for l in open("aws-emulator-evaluation-suite/manifest.jsonl")]
print("[1] トップレベルのキー:", list(scores))
print(" commit:", scores["commit"], " scoredAt:", scores["scoredAt"])
print(" units:", len(units), collections.Counter(u["kind"] for u in units))
print(" outcome:", collections.Counter(u["outcome"] for u in units))
tests = [u for u in units if u["kind"] == "behavioral"]
ids = {m["check_id"] for m in manifest}
print("[2] behavioral の unitId のうち manifest.jsonl の check_id と一致:",
sum(u["unitId"] in ids for u in tests), "/", len(tests))
missing = [u["unitId"] for u in tests if u["unitId"] not in ids]
print(" manifest.jsonl にない unitId:", len(missing), " うち _smoke. で始まるもの:", sum(i.startswith("_smoke.") for i in missing))
print("[3] サービスごとの fidelity テストの結果(kind=behavioral、サービス名に __ が入らないテスト)")
by = collections.defaultdict(collections.Counter)
for u in tests:
svc = u["unitId"].split(".")[0]
if "__" not in svc and not svc.startswith("_"):
by[svc][u["outcome"]] += 1
for svc in ["dynamodb", "s3", "sqs", "ssm", "logs"]:
c = by[svc]
print(f" {svc:9} pass {c['pass']:4} / 全体 {sum(c.values()):4}(fail {c['fail']}, skipped {c['skipped']})")
print("[4] ssm:DescribeOpsItems を呼ぶ fidelity テスト(manifest.jsonl)と結果")
result = {u["unitId"]: u["outcome"] for u in tests}
for m in manifest:
if "ssm:DescribeOpsItems" in m["operations"]:
print(" ", m["check_id"], m["labels"], "->", result.get(m["check_id"]))
print("[5] wire の記録(expected と actual)")
byid = {u["unitId"]: u for u in units}
for uid in ("ssm-ops-item-lifecycle-and-query", "s3-mvb-rename-object-general-purpose"):
u = byid[uid]; d = u["detail"]
print(" ", uid, u["kind"], u["outcome"], d["operation"],
"expected", d["expected"], "actual", d["actual"], "mismatch", d["mismatch"])
print("[6] actual が 501 の記録の数と、そのうち expected も 501 の記録")
a501 = [u for u in units if u["detail"].get("actual", {}).get("status") == 501]
print(" actual 501:", len(a501), collections.Counter(u["kind"] for u in a501))
for u in a501:
if u["detail"].get("expected", {}).get("status") == 501:
print(" expected も 501:", u["unitId"], u["detail"]["operation"])
結果を、出力の番号の順に読んでいきます。
ファイルの全体と、テストの一覧との対応
[1] トップレベルのキー: ['commit', 'schemaVersion', 'scoredAt', 'units']
commit: c479b9c7e416e3201fdeba8fafd9c65b0eed8c74 scoredAt: 2026-11-13T23:52:50.952383+00:00
units: 43548 Counter({'behavioral': 32839, 'wire': 10709})
outcome: Counter({'pass': 27430, 'fail': 16035, 'skipped': 83})
[2] behavioral の unitId のうち manifest.jsonl の check_id と一致: 29676 / 32839
manifest.jsonl にない unitId: 3163 うち _smoke. で始まるもの: 17
commit は、aws-emulator -v のコミットと一致しました。units の1件が1つの記録で、kind は behavioral と wire の2種類、outcome は pass、fail、skipped の3種類でした。
評価スイートの README には、check_id が実行結果や公開されたスコアで使う ID だと書かれています。behavioral の 32,839 件のうち 29,676 件の unitId が、取得した manifest.jsonl(2026-11-11 のコミット、29,676 行)の check_id と一致しました。残りの 3,163 件は manifest.jsonl に見つかりませんでした(_smoke. で始まる 17 件を含みます)。
サービスごとの結果
[3] サービスごとの fidelity テストの結果(kind=behavioral、サービス名に __ が入らないテスト)
dynamodb pass 844 / 全体 1073(fail 229, skipped 0)
s3 pass 826 / 全体 930(fail 104, skipped 0)
sqs pass 288 / 全体 306(fail 18, skipped 0)
ssm pass 86 / 全体 282(fail 196, skipped 0)
logs pass 396 / 全体 460(fail 64, skipped 0)
評価スイートの README では、複数のサービスをまたぐテストのサービス名を a__b の形で書くことになっています。そこで、unitId の先頭のサービス名に __ が入らないテストだけを数えました。公式ガイドの言う「サービスごとのスコア」と同じ数え方かは、説明がないので分かりません。この版では、SSM のテストは 282 件のうち 86 件しか通っていませんでした。
使う操作のテストを探す
[4] ssm:DescribeOpsItems を呼ぶ fidelity テスト(manifest.jsonl)と結果
cloudwatch__ssm.xsvc-cloudwatch-3-alarm-ssm-opsitem-action-creates-opsitem-and-disabled-twin-blocked ['axis:negative-delivery', 'dp:api', 'plane:data'] -> fail
ssm.ops-item-delete-and-events-list ['axis:happy-path', 'plane:control'] -> fail
operations に ssm:DescribeOpsItems を含むテストは2件で、2件とも fail でした。labels の意味は、評価スイートの docs/TAXONOMY.md に書かれています。
| ラベル | 意味 |
|---|---|
plane:control |
リソースの作成・取得・更新・削除といった、設定の操作を確かめる |
plane:data |
データの書き込みや読み出し、メッセージの送信など、リソースを使う操作を確かめる |
dp:api |
署名付きの AWS の API そのものが、データを扱う経路になっている |
axis:happy-path |
操作が成功し、その結果が AWS と同じになるかを確かめる |
axis:negative-delivery |
届いてはいけないものが、送り先に届かないことを確かめる |
公式ガイドの fidelity の説明には、いまの AWS Emulator は control plane の API がもっとも AWS に近く、data plane はサービスによって差がある、と書かれています。
失敗したテストの中身を見る
[5] wire の記録(expected と actual)
ssm-ops-item-lifecycle-and-query wire fail DescribeOpsItems expected {'status': 200} actual {'errorCode': 'NotImplemented', 'status': 501} mismatch status
s3-mvb-rename-object-general-purpose wire fail RenameObject expected {'errorCode': 'NotImplemented', 'status': 501} actual {'errorCode': 'NotImplemented', 'status': 501} mismatch body
[6] actual が 501 の記録の数と、そのうち expected も 501 の記録
actual 501: 1059 Counter({'wire': 1059})
expected も 501: s3-mvb-rename-object-general-purpose RenameObject
wire の記録には、どの操作で、何が食い違ったかが入っていました。公式ガイドでは、fidelity テストは AWS での振る舞いを確かめるものとされています。このことと項目名から、expected は AWS で記録された応答、actual は AWS Emulator の応答だと読めます。
この読み方に従うと、DescribeOpsItems は、AWS では 200 が返るところで AWS Emulator が 501 を返しています。汎用バケットでの RenameObject は、AWS でも 501 の NotImplemented で、ステータスとエラーコードは一致しています(本文が違うので、テストとしては fail)。AWS Emulator が 501 を返した記録は 1,059 件あり、そのうち AWS 側も 501 だったのは、この RenameObject の1件だけでした。
テストが見つからない操作については、公式ガイドにある、プロジェクトが AWS Emulator で動くかを確かめる手順で、AWS Emulator と AWS の両方で動かして比べるよう案内されています。テストがないのは、まだ確かめられていないというだけで、正しく動く場合もあるとも書かれています。AWS Emulator の動きが AWS と違うときは、aws-emulator リポジトリの Issues に、送ったリクエスト、返ってきた応答、期待した結果を書いて報告できます。




