Why am I getting a Runtime.MarshalError when using this code in Zapier?
Asked Answered
L

2

8

The following code is giving me:

Runtime.MarshalError: Unable to marshal response: {'Yes'} is not JSON serializable

from calendar import monthrange

def time_remaining_less_than_fourteen(year, month, day):
    a_year = int(input['year'])
    b_month = int(input['month'])
    c_day = int(input['day'])
    days_in_month = monthrange(int(a_year), int(b_month))[1]
    time_remaining = ""

    if (days_in_month - c_day) < 14:
        time_remaining = "No"
        return time_remaining

    else:
        time_remaining = "Yes"
        return time_remaining


output = {time_remaining_less_than_fourteen((input['year']), (input['month']), (input['day']))}

#print(output)

When I remove {...} it then throws: 'unicode' object has no attribute 'copy'

Lindon answered 28/1, 2019 at 17:28 Comment(0)
B
27

I encountered this issue when working with lambda transformation blueprint kinesis-firehose-process-record-python for Kinesis Firehose which led me here. Thus I will post a solution to anyone who also finds this questions when having issues with the lambda.

The blueprint is:

from __future__ import print_function

import base64

print('Loading function')


def lambda_handler(event, context):
    output = []

    for record in event['records']:
        print(record['recordId'])
        payload = base64.b64decode(record['data'])

        # Do custom processing on the payload here

        output_record = {
            'recordId': record['recordId'],
            'result': 'Ok',
            'data': base64.b64encode(payload)
        }
        output.append(output_record)

    print('Successfully processed {} records.'.format(len(event['records'])))

    return {'records': output}

The thing to note is that the Firehose lambda blueprints for python provided by AWS are for Python 2.7, and they don't work with Python 3. The reason is that in Python 3, strings and byte arrays are different.

The key change to make it work with lambda powered by Python 3.x runtime was:

changing

'data': base64.b64encode(payload)

into

'data': base64.b64encode(payload).decode("utf-8")

Otherwise, the lambda had an error due to inability to serialize JSON with byte array returned from base64.b64encode.

Babar answered 21/1, 2020 at 13:14 Comment(4)
Thanks Marcin, I was having this exact same issue and your answer just saved me a lot of headaches. That Python 2.7 blueprint led me to believe that Kinesis was expecting bytes as the record's data. Conversion to str didn't even cross my mind. Quick suggestion: base64.b64encode(payload).decode("utf-8") could be simply base64.b64encode(payload).decode(). Base64 data is, by definition, ASCII. ASCII itself is UTF-8, which is the default value for the encoding parameter in Python 3. Specifying the codec there is just redundant.Locale
@Rafa Glad my answer helped, and thanks for the good suggestion.Babar
Not sure whether you saved my life literally or merely figuratively, but thanks for this answer either way.^^ +1Pinworm
Thanks for saving my time @Marcin. Had the same issue and your solution worked.Scrim
C
3

David here, from the Zapier Platform team.

Per the docs:

output: A dictionary or list of dictionaries that will be the "return value" of this code. You can explicitly return early if you like. This must be JSON serializable!

In your case, output is a set:

>>> output = {'Yes'}
>>> type(output)
<class 'set'>
>>> json.dumps(output)
Object of type set is not JSON serializable

To be serializable, you need a dict (which has keys and values). Change your last line to include a key and it'll work like you expect:

#         \ here /
output = {'result': time_remaining_less_than_fourteen((input['year']), (input['month']), (input['day']))}
Cabriolet answered 28/1, 2019 at 19:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.