Phito
Phito

Reputation: 1195

Malformed Lambda proxy response: string indices must be integers

I am trying to write a serverless back-end for an application with AWS Lambda, and am running into the error in the title. The error occurs when testing with API Gateway proxy integration, but the function works fine when tested in the Lambda console.

Here is the error:

{  
   "errorMessage":"string indices must be integers",
   "errorType":"TypeError",
   "stackTrace":[  
      [  
         "/var/task/auth_login.py",
         17,
         "lambda_handler",
         "response = get_user(payload)"
      ],
      [  
         "/var/task/shifty_utils/__init__.py",
         22,
         "get_user",
         "table = dynamo.Table(user['company'] + '_users')"
      ]
   ]
}

Here is context for where it occurs:

def lambda_handler(event, context):
    payload = event['body']
    response = get_user(payload)

def get_user(user):
    try:
        table = dynamo.Table(user['company'] + '_users')
        response = table.get_item(
            Key={
                'userId': user['userId'],
                'position': user['position']
            }
        )
    except ClientError as e:
        print(e.response['Error']['Message'])
        return {'message': e.response['Error']['Message']}
    else:
        return response

Basically proxy integration seems to be reading in the event object as a JSON formatted string, as opposed to a dict, but here's what happens if I adjust my code for that:

{  
   "errorMessage":"the JSON object must be str, bytes or bytearray, not 'dict'",
   "errorType":"TypeError",
   "stackTrace":[  
      [  
         "/var/task/auth_login.py",
         15,
         "lambda_handler",
         "payload = json.loads(event)"
      ],
      [  
         "/var/lang/lib/python3.6/json/__init__.py",
         348,
         "loads",
         "'not {!r}'.format(s.__class__.__name__))"
      ]
   ]
}

I can't win. Any help is appreciated.

Upvotes: 6

Views: 14904

Answers (3)

Kai Peng
Kai Peng

Reputation: 99

This is because event['body'] is not a dict but a str. (I ran into this problem when decoding an SQS triggered event)

In case if anyone ran into a problem when a value of json.loads(event['body']) is again not dict but str, here is a solution that decodes str to dict recursively.

import json

def to_dict(obj : object) -> dict:
    """ Serialize Object to Dictionary Recursively

    Arguments:
        obj {object} -- string, list, or dictionary to be serialize

    Returns:
        dict -- Serialized Dictionary
    """

    if isinstance(obj, dict):
        data = {}
        for k, v in obj.items():
            data[k] = to_dict(v)
        return data

    elif hasattr(obj, "_ast"):
        return to_dict(obj._ast())

    elif hasattr(obj, "__iter__") and not isinstance(obj, str):
        return [to_dict(v) for v in obj]

    elif hasattr(obj, "__dict__"):
        data = {key : to_dict(value) for key, value in obj.__dict__.items() if 
                  not callable(value) and not key.startswith('_')}

    elif isinstance(obj, str):
        try:
            data = {}
            obj = json.loads(obj)
            for k, v in obj.items():
                data[k] = to_dict(v)
                return data
        except:
            return obj
    else:
        return obj

Example Usage:

test = {'Records': ['{"s3": "{\\"bucket\\": \\"bucketname\\"}"}', '{"s3": "{\\"bucket\\": \\"bucketname\\"}"}']}

print(to_dict(test)['Records'][0]['s3']['bucket'])

This should print "bucketname".

Upvotes: 6

user3682983
user3682983

Reputation: 177

When dealing with json, python provides 2 std functions:

https://docs.python.org/3/library/json.html#json.dumps

Serialize obj to a JSON formatted str using this conversion table. The arguments have the same meaning as in dump().

https://docs.python.org/3/library/json.html#json.loads

Deserialize s (a str, bytes or bytearray instance containing a JSON document) to a Python object using this conversion table.

What you need here is the latest:

import json
payload = json.loads(event['body']

event['body'] is probably a json str so for accessing it´s values you fist will need to convert it to a python obj via `json.loads

Upvotes: 3

Oluwafemi Sule
Oluwafemi Sule

Reputation: 38982

You've identified the issue. However you're trying to convert a dict to dict.

This is what you have:

json.loads(event) # event is a dict

The body part as you have rightly identified is what is getting in as str.

This is what you should have:

json.loads(event['body'])

One more step is to make it client-agnostic.

if isinstance(event['body'], (unicode, str)):
    body = json.loads(event['body'])

Upvotes: 20

Related Questions