hemagso
hemagso

Reputation: 41

boto3 and lambda: Invalid type for parameter KeyConditionExpression when using DynamoDB resource and localstack

I'm having very inconsistent results when trying to use boto3 dynamodb resources from my local machine vs from within a lambda function in localstack. I have the following simple lambda handler, that just queries a table based on the Hash Key:

import boto3
from boto3.dynamodb.conditions import Key

def handler(event, context):
    dynamodb = boto3.resource(
        "dynamodb", endpoint_url=os.environ["AWS_EP"]
    )
    table = dynamodb.Table("precalculated_scores")
    items = table.query(
        KeyConditionExpression=Key("customer_id").eq(event["customer_id"])
    )
    return items

The environment variable "AWS_EP" is set to my localstack DNS when protyping (http://localstack:4566).

When I call this lamdba I get the following error:

{
    "errorMessage": "Parameter validation failed:\nInvalid type for parameter KeyConditionExpression, value: <boto3.dynamodb.conditions.Equals object at 0x7f7440201960>, type: <class 'boto3.dynamodb.conditions.Equals'>, valid types: <class 'str'>",
    "errorType": "ParamValidationError",
    "stackTrace": [
        "  File \"/opt/code/localstack/localstack/services/awslambda/lambda_executors.py\", line 1423, in do_execute\n    execute_result = lambda_function_callable(inv_context.event, context)\n",
        "  File \"/opt/code/localstack/localstack/services/awslambda/lambda_api.py\", line 782, in exec_local_python\n    return inner_handler(event, context)\n",
        "  File \"/var/lib/localstack/tmp/lambda_script_l_dbef16b3.py\", line 29, in handler\n    items = table.query(\n",
        "  File \"/opt/code/localstack/.venv/lib/python3.10/site-packages/boto3/resources/factory.py\", line 580, in do_action\n    response = action(self, *args, **kwargs)\n",
        "  File \"/opt/code/localstack/.venv/lib/python3.10/site-packages/boto3/resources/action.py\", line 88, in __call__\n    response = getattr(parent.meta.client, operation_name)(*args, **params)\n",
        "  File \"/opt/code/localstack/.venv/lib/python3.10/site-packages/botocore/client.py\", line 514, in _api_call\n    return self._make_api_call(operation_name, kwargs)\n",
        "  File \"/opt/code/localstack/.venv/lib/python3.10/site-packages/botocore/client.py\", line 901, in _make_api_call\n    request_dict = self._convert_to_request_dict(\n",
        "  File \"/opt/code/localstack/.venv/lib/python3.10/site-packages/botocore/client.py\", line 962, in _convert_to_request_dict\n    request_dict = self._serializer.serialize_to_request(\n",
        "  File \"/opt/code/localstack/.venv/lib/python3.10/site-packages/botocore/validate.py\", line 381, in serialize_to_request\n    raise ParamValidationError(report=report.generate_report())\n"
    ]
}

Which is a weird error - From what I researched on other question it usually happens when using the boto3 client, but I am using boto3 resources. Furthermore, when I run the code locally in my machine it runs fine.

At first I thought that it might be due to different versions for boto3 (My local machine is using version 1.24.96, while the version inside the lambda runtime is 1.16.31). However I downgraded my local version to the same as the one in the runtime, and I keep getting the same results.

After some answers on this question I managed to get the code working against actual AWS services, but it still won't work when running against localstack.

Am I doing anything wrong? Os might this be a bug with localstack?

--- Update 1 ---

Changing the return didn't solve the problem:

return {"statusCode": 200, "body": json.dumps(items)}

--- Update 2 ---

The code works when running against actual AWS services instead of running against localstack. Updating the question with this information.

Upvotes: 0

Views: 1004

Answers (1)

Leeroy Hannigan
Leeroy Hannigan

Reputation: 19883

This works fine from both my local machine and Lambda:

import json, boto3
from boto3.dynamodb.conditions import Key


def lambda_handler(event, context):
    dynamodb = boto3.resource(
    "dynamodb",
     endpoint_url="https://dynamodb.eu-west-1.amazonaws.com"
    )
    table = dynamodb.Table("test")
    items = table.query(
        KeyConditionExpression=Key("pk").eq("1")
    )
    print(items)

    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

Also be sure that event["customer_id"] is in fact a string value as expected by the eq function.

I would check to ensure you have the endpoint setup correctly and that you have the current version deployed.

It may also be the fact you are trying to return the results of your API call via the handler, instead of a proper JSON response as expected:

    return {
        'statusCode': 200,
        'body': json.dumps(items)
    }

Upvotes: 0

Related Questions