Boto3でLambdaを呼び出すと”Unable to marshal response: Object of type StreamingBody is not JSON serializable”エラーとなる
こんにちは、CX事業本部の若槻です。
今回は、Boto3でLambdaを呼び出した時の戻り値の処理がUnable to marshal response: Object of type StreamingBody is not JSON serializable
エラーとなったので対処した話です。
事象
以下のように実行される側(scan_func
)と実行する側(execute_func
)のLambdaがあり、後者から前者を呼び出してscan_func
からの戻り値を得たいです。
- 実行される側(
scan_func
)
import json
import boto3
def lambda_handler(event, context):
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('wakatsuki_test')
resp = table.scan()
return json.dumps(resp['Items'])
- 実行する側(
invoke_func
)
import json
import boto3
def lambda_handler(event, context):
resp = boto3.client('lambda').invoke(
FunctionName='scan_func',
InvocationType='RequestResponse'
)
return resp
しかしinvoke_func
を実行したところ、下記のUnable to marshal response: Object of type StreamingBody is not JSON serializable
エラーが発生してしまいます。
Response:
{
"errorMessage": "Unable to marshal response: Object of type StreamingBody is not JSON serializable",
"errorType": "Runtime.MarshalError"
}
調査
Boto3のドキュメントを確認してみます。
ドキュメントによると、boto3.client('lambda').invoke()
のレスポンスは以下のような形式を取るとのことです。
Response Syntax
{
'StatusCode': 123,
'FunctionError': 'string',
'LogResult': 'string',
'Payload': StreamingBody(),
'ExecutedVersion': 'string'
}
このうち、関数からのレスポンスは`Payload (StreamingBody)`に含まれるとのことです。今回エラーとなっているのはこの部分のJSONシリアライズです。
> ・ Payload (StreamingBody) --
The response from the function, or an error object.
ではこの`StreamingBody`はどんなデータ型であるかというと、botocoreのドキュメントに記載がありました。
- [https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/response.html)
>class botocore.response.StreamingBody(`raw_stream, content_length`)
>Wrapper class for an http response body.
どうやら`read()`メソッドを使えばStreamingBodyに含まれるレスポンスボディを取得できそうです。
>`read(amt=None)`
>Read at most amt bytes from the stream.
If the amt argument is omitted, read all data.
## 解決
調査を踏まえて、Lambdaを呼び出したレスポンスを`resp['Payload'].read()`のようにパースすることにより、`scan_func`からの戻り値を得ることができるようになりました。
```lambda_function.py
import json
import boto3
def lambda_handler(event, context):
resp = boto3.client('lambda').invoke(
FunctionName='test_wakatsuki',
InvocationType='RequestResponse'
)
return resp['Payload'].read()
Response:
"[{\"id\": \"aaa\"}]"
おわりに
あまり使ったことがないメソッドやデータを扱うときは、今回のようにまずデータの型を調査する癖をつけたいですね。
以上