Error: Runtime exited without providing a reason in python lambda

Viewed 10299

This code is used to do following

  1. This lambda gets triggered via an event rule
  2. Event is sent when and instance state goes to running/terminated
  3. When instance is running the instance attributes are saved to dynamodb table
  4. Route53 record sets are created / deleted when instance is in running/ terminated state.
  5. Now the lambda created records when instance is launched and throws below error

RequestId: 9f4fb9ed-88db-442a-bc4f-079744f5bbcf Error: Runtime exited without providing a reason Runtime.ExitError

import ipaddress
import os
import time
from datetime import datetime
from typing import Dict, List, Optional

import boto3
from botocore.exceptions import ClientError, ParamValidationError
from pynamodb.attributes import UnicodeAttribute, UTCDateTimeAttribute
from pynamodb.exceptions import DoesNotExist
from pynamodb.models import Model



def lambda_handler(event, context):
    """Registers or de-registers private DNS resource records for a given EC2 instance."""

    # Retrieve details from invocation event object.
    try:
        account_id = event["account"]
        instance_id = event["detail"]["instance-id"]
        instance_region = event["region"]
        instance_state = event["detail"]["state"]
    except KeyError as err:
        raise RuntimeError(
            f"One or more required fields missing from event object {err}"
        )

    print(
        f"EC2 instance {instance_id} changed to state `{instance_state}` in account "
        f"{account_id} and region {instance_region}."
    )
    print(f"Creating a new aws session in {instance_region} for account {account_id}.")

    target_session = aws_session(
    region=instance_region, account_id=account_id, assume_role=ASSUME_ROLE_NAME,
    )

    print(f"Retrieving instance and VPC attributes for instance {instance_id}.")
    instance_resource = get_instance_resource(instance_id, target_session)
    vpc_resource = get_vpc_resource(instance_resource.vpc_id, target_session)
    route53_client = target_session.client("route53")

    print(f"Retrieving DNS configuration from VPC {instance_resource.vpc_id}.")
    forward_zone = get_vpc_domain(vpc_resource)

    print(f"Calculating reverse DNS configuration for instance {instance_id}.")
    reverse_lookup = determine_reverse_lookup(instance_resource, vpc_resource)

    if instance_state == "running":
        print(f"Building DNS registration record for instance {instance_id}.")
        #vpc_resource = get_vpc_resource(instance_resource.vpc_id, target_session)
        #print(f"Retrieving DNS configuration from VPC {instance_resource.vpc_id}.")
        #forward_zone = get_vpc_domain(vpc_resource)
        #print(f"Calculating reverse DNS configuration for instance {instance_id}.")
        #reverse_lookup = determine_reverse_lookup(instance_resource, vpc_resource)
        record = Registration(
            account_id=account_id,
            hostname=generate_hostname(instance_resource),
            instance_id=instance_resource.id,
            forward_zone=forward_zone,
            forward_zone_id=get_zone_id(forward_zone, route53_client),
            private_address=instance_resource.private_ip_address,
            region=instance_region,
            reverse_hostname=reverse_lookup["Host"],
            reverse_zone=reverse_lookup["Zone"],
            reverse_zone_id=get_zone_id(reverse_lookup["Zone"], route53_client),
            vpc_id=instance_resource.vpc_id,
        )
        print(record)
        try:
            if record.forward_zone_id is not None:
                manage_resource_record(record, route53_client)
            if record.forward_zone_id and record.reverse_zone_id is not None:
                manage_resource_record(record, route53_client, record_type="PTR")
        except RuntimeError as err:
            print(f"An error occurred while creating records: {err}")
            exit(os.EX_IOERR)

        if record.forward_zone_id:
            print(
                f"Saving DNS registration record to database for instance {instance_id}."
            )
            record.save()
        else:
            print(
                f"No matching hosted zone for {record.forward_zone} associated "
                f"with {record.vpc_id}."
            )
    else:
        try:
            print(
                f"Getting DNS registration record from database for instance {instance_id}."
            )
            record = Registration.get(instance_id)
            if record.forward_zone_id is not None:
                manage_resource_record(record, route53_client, action="DELETE")
            if record.reverse_zone_id is not None:
                manage_resource_record(record, route53_client, record_type="PTR", action="DELETE")
            print(
                "Deleting DNS registration record from database for "
                f"instance {instance_id}."
            )
            record.delete()
        except DoesNotExist:
            print(f"A registration record for instance {instance_id} does not exist.")
            exit(os.EX_DATAERR)
        except RuntimeError as err:
            print(f"An error occurred while removing resource records: {err}")
            exit(os.EX_IOERR)

    exit(os.EX_OK)
3 Answers

I had this problem with a dotnet lambda. Turns out it'd run out of memory. Raising the memory ceiling allowed it to pass.

exit(os.EX_OK) statement in last line was causing this. Removing this line resolved my issue.

What I did to get around this, but still have an exit code if there was errors detected was the following:

def main(event=None, context=None):
    logger.info('Starting Lambda')

    error_count = 0
    s3_objects  = parse_event(event)

    for s3_object in s3_objects:
        logger.info('Parsing s3://{}/{}'.format(s3_object['bucket'], s3_object['key']))
        error_count += parse_object(object_json)
    
    logger.info('Total Errors: {}'.format(error_count))

    if error_count > 255:
        error_count = 255

    logger.info('Exiting lambda')

    # exit if error_count (lambda doesn't like sys.exit(0))
    if error_count > 0:
        sys.exit(error_count)

TL;DR - Exit only if an error is detected.

Related