Reputation: 1169
I have a lambda fucntion where I am sending email and the mail template is fetching as an object from an S3 bucket. I have ran the code locally in my computer and I works fine. When I paste it inside my lambda function, it shows following error.
Response:
{
"errorMessage": "Parameter validation failed:\nInvalid type for parameter Message.Body.Html.Data, value: b\"<html>\\n<head></head>\\n<body>\\n <h1>Amazon SES Test (SDK for Python)</h1>\\n <p>This email was sent with\\n <a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the\\n <a href='https://aws.amazon.com/sdk-for-python/'>\\n AWS SDK for Python (Boto)</a>.</p>\\n</body>\\n</html>\", type: <class 'bytes'>, valid types: <class 'str'>",
"errorType": "ParamValidationError",
"stackTrace": [
[
"/var/task/lambda_function.py",
65,
"lambda_handler",
"Source=SENDER,"
],
[
"/var/runtime/botocore/client.py",
314,
"_api_call",
"return self._make_api_call(operation_name, kwargs)"
],
[
"/var/runtime/botocore/client.py",
586,
"_make_api_call",
"api_params, operation_model, context=request_context)"
],
[
"/var/runtime/botocore/client.py",
621,
"_convert_to_request_dict",
"api_params, operation_model)"
],
[
"/var/runtime/botocore/validate.py",
291,
"serialize_to_request",
"raise ParamValidationError(report=report.generate_report())"
]
]
}
Request ID:
"7b13a612-97f6-4278-9825-724abeaa3b51"
Function Logs:
START RequestId: 7b13a612-97f6-4278-9825-724abeaa3b51 Version: $LATEST
Parameter validation failed:
Invalid type for parameter Message.Body.Html.Data, value: b"<html>\n<head></head>\n<body>\n <h1>Amazon SES Test (SDK for Python)</h1>\n <p>This email was sent with\n <a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the\n <a href='https://aws.amazon.com/sdk-for-python/'>\n AWS SDK for Python (Boto)</a>.</p>\n</body>\n</html>", type: <class 'bytes'>, valid types: <class 'str'>: ParamValidationError
Traceback (most recent call last):
File "/var/task/lambda_function.py", line 65, in lambda_handler
Source=SENDER,
File "/var/runtime/botocore/client.py", line 314, in _api_call
return self._make_api_call(operation_name, kwargs)
File "/var/runtime/botocore/client.py", line 586, in _make_api_call
api_params, operation_model, context=request_context)
File "/var/runtime/botocore/client.py", line 621, in _convert_to_request_dict
api_params, operation_model)
File "/var/runtime/botocore/validate.py", line 291, in serialize_to_request
raise ParamValidationError(report=report.generate_report())
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid type for parameter Message.Body.Html.Data, value: b"<html>\n<head></head>\n<body>\n <h1>Amazon SES Test (SDK for Python)</h1>\n <p>This email was sent with\n <a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the\n <a href='https://aws.amazon.com/sdk-for-python/'>\n AWS SDK for Python (Boto)</a>.</p>\n</body>\n</html>", type: <class 'bytes'>, valid types: <class 'str'>
END RequestId: 7b13a612-97f6-4278-9825-724abeaa3b51
REPORT RequestId: 7b13a612-97f6-4278-9825-724abeaa3b51 Duration: 1608.47 ms Billed Duration: 1700 ms Memory Size: 128 MB Max Memory Used: 71 MB
I have checked when I load the template from S3 bucket it shows the following error in lambda. When I declare the BODY_HTML inside my code and not fetch it from S3 in Lambda, it works fine. Here is my code now :
import json
import os
import boto3
from botocore.exceptions import ClientError
SENDER = "***********"
RECIPIENT = "************"
AWS_REGION = "us-east-1"
def lambda_handler(event, context):
CHARSET = "UTF-8"
client = boto3.client('ses',aws_access_key_id=******,
aws_secret_access_key=*****,region_name='us-east-1')
s3_client = boto3.client('s3',aws_access_key_id=*********,
aws_secret_access_key=***********,region_name='us-east-1')
s3_response_object = s3_client.get_object(Bucket='my s3 bucket', Key='template.html')
object_content = s3_response_object['Body'].read()
BODY_HTML = object_content
SUBJECT = "sqs-poc-lambda test email"
BODY_TEXT = ("This is a test email for sqs-poc-lambda"
)
try:
#Provide the contents of the email.
response = client.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER,
)
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])
)
It should return the message ID successfully in the Function Logs
. Any help on this? I cannot see any logic behind this.
Upvotes: 1
Views: 23073
Reputation: 14502
It is hard to believe that the second code works because it contains the same error that is reported in the first error log. I have copied the code and it fails in the same way.
Issue can be spot by reading this message from error log.
Invalid type for parameter Message.Body.Html.Data, value: b"... ", type: class bytes, valid types: class 'str': ParamValidationError.
Body should be of type string but object_content
object_content = s3_response['Body'].read()
is of type bytes. You need to convert it to string. E.g. BODY_HTML = str(object_content)
IMPORTANT
Do NOT store permanent credentials in your lambda code.
client = boto3.client('ses',aws_access_key_id=******,
aws_secret_access_key=*****,region_name='us-east-1')
s3_client = boto3.client('s3',aws_access_key_id=*********,
aws_secret_access_key=***********,region_name='us-east-1')
Give permissions to your lambda function via Lambda Execution Role.
And if you need to provide cross account access to your lambda function then include sts:AssumeRole
permissions in that role and then use temporary credentials obtained by calling sts:AssumeRole
client = boto3.client(
'ses',
aws_access_key_id=...,
aws_secret_access_key=...,
aws_session_token=... // !!!
)
But again, NEVER store permanent credentials in your function code. Are you sure that next time you place your code in some public repository you will not forget to replace those values with ******
? Each time?
Upvotes: 5