# Fix AWS API Gateway 500 Error

Your API Gateway is returning 500 Internal Server Error responses, and the generic message "Internal Server Error" tells you nothing about what's actually wrong. The problem could be in your Lambda function, your backend integration, request/response mapping, or even API Gateway configuration itself.

Let's systematically track down the root cause.

Diagnosis Commands

First, get basic information about your API:

bash
aws apigateway get-rest-apis \
  --query 'items[*].[id,name,version]' \
  --output table

Get details about a specific API:

bash
aws apigateway get-rest-api \
  --rest-api-id abc123def4 \
  --query '[id,name,version,createdDate]'

List the resources and methods:

bash
aws apigateway get-resources \
  --rest-api-id abc123def4 \
  --query 'items[*].[id,path,resourceMethods]'

Check the method configuration:

bash
aws apigateway get-method \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method GET \
  --query '[httpMethod,authorizationType,integration.type,integration.uri]'

Enable access logging if not already enabled:

bash
aws apigateway update-stage \
  --rest-api-id abc123def4 \
  --stage-name prod \
  --patch-operations op=replace,path=/accessLogSettings/destinationArn,value=arn:aws:logs:us-east-1:123456789012:log-group:api-gateway-logs,op=replace,path=/accessLogSettings/format,value='{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","caller":"$context.identity.caller","user":"$context.identity.user","requestTime":"$context.requestTime","httpMethod":"$context.httpMethod","resourcePath":"$context.resourcePath","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength","integrationError":"$context.integrationErrorMessage"}'

Create the log group first if needed:

bash
aws logs create-log-group \
  --log-group-name /aws/apigateway/abc123def4/prod

View recent access logs:

bash
aws logs filter-log-events \
  --log-group-name /aws/apigateway/abc123def4/prod \
  --start-time $(date -u -d '1 hour ago' +%s)000 \
  --filter-pattern "500" \
  --query 'events[*].message' \
  --output text

Check CloudWatch metrics for error patterns:

bash
aws cloudwatch get-metric-statistics \
  --namespace AWS/ApiGateway \
  --metric-name 5XXError \
  --dimensions Name=ApiName,Value=my-api Name=Stage,Value=prod \
  --start-time $(date -u -d '24 hours ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 3600 \
  --statistics Sum \
  --output table

Common Causes and Solutions

Lambda Integration Errors

For Lambda integrations, check the Lambda logs:

bash
aws logs filter-log-events \
  --log-group-name /aws/lambda/my-function \
  --start-time $(date -u -d '1 hour ago' +%s)000 \
  --query 'events[*].message' \
  --output text

Check for Lambda errors:

bash
aws cloudwatch get-metric-statistics \
  --namespace AWS/Lambda \
  --metric-name Errors \
  --dimensions Name=FunctionName,Value=my-function \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 300 \
  --statistics Sum

Common Lambda issues:

Incorrect response format:

Lambda must return a specific response format for API Gateway:

javascript
// Correct format
exports.handler = async (event) => {
  return {
    statusCode: 200,
    headers: {
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ message: "Success" })
  };
};

Timeout:

bash
aws lambda get-function-configuration \
  --function-name my-function \
  --query 'Timeout'

Increase timeout if needed:

bash
aws lambda update-function-configuration \
  --function-name my-function \
  --timeout 30

Memory issues:

bash
aws cloudwatch get-metric-statistics \
  --namespace AWS/Lambda \
  --metric-name MemoryUtilization \
  --dimensions Name=FunctionName,Value=my-function \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
  --period 300 \
  --statistics Maximum

Backend Integration Issues

For HTTP integrations, test the backend directly:

```bash # Test with curl curl -v https://backend-api.example.com/endpoint

# Check backend health curl -X GET https://backend-api.example.com/health ```

Check integration configuration:

bash
aws apigateway get-integration \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method GET \
  --query '[type,uri,httpMethod,requestTemplates,responseTemplates]'

Connection timeout:

If your backend is slow, increase the integration timeout:

bash
aws apigateway update-integration \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method GET \
  --patch-operations op=replace,path=/timeoutInMillis,value=29000

Maximum timeout is 29 seconds for API Gateway.

Backend unavailable:

Check if your backend endpoints are reachable:

```bash # For ALB/NLB backends aws elbv2 describe-target-health \ --target-group-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/my-targets/abc123

# For EC2 backends aws ec2 describe-instance-status \ --instance-ids i-1234567890abcdef0 ```

Request/Response Mapping Issues

VTL (Velocity Template Language) mapping errors can cause 500s.

Check request templates:

bash
aws apigateway get-integration \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method POST \
  --query 'requestTemplates'

Test mapping templates:

bash
aws apigateway test-invoke-method \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method POST \
  --body '{"test": "data"}' \
  --query '[status,body,log]'

Common VTL errors:

Syntax error in template:

```velocity ## Bad - missing closing brace #set($input = $input.json('$')

Good #set($input = $input.json('$')) ```

Missing required fields:

velocity
## Ensure all required fields are present
{
  "statusCode": 200,
  "body": $input.json('$')
}

Update the integration with correct templates:

bash
aws apigateway put-integration \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method POST \
  --type AWS_PROXY \
  --integration-http-method POST \
  --uri arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:123456789012:function:my-function/invocations \
  --request-templates '{"application/json": "#set($inputRoot = $input.path('$'))\n{\n  \"body\": $input.json('$')\n}"}'

Authorizer Errors

Custom authorizers can cause 500s if they fail:

bash
aws apigateway get-authorizers \
  --rest-api-id abc123def4 \
  --query 'items[*].[id,name,type,authorizerUri]'

Test authorizer directly:

bash
aws lambda invoke \
  --function-name my-authorizer \
  --payload '{"type":"TOKEN","authorizationToken":"Bearer test-token"}' \
  --cli-binary-format raw-in-base64-out \
  authorizer-response.json && cat authorizer-response.json

Check authorizer logs:

bash
aws logs filter-log-events \
  --log-group-name /aws/lambda/my-authorizer \
  --start-time $(date -u -d '1 hour ago' +%s)000 \
  --query 'events[*].message'

Throttling

API Gateway has default limits that can cause 500s:

bash
aws apigateway get-account \
  --query 'throttleSettings'

Check usage plans and throttling:

bash
aws apigateway get-usage-plans \
  --query 'items[*].[id,name,throttle]'

Increase limits if needed (request AWS support for account-level increases):

bash
aws apigateway update-stage \
  --rest-api-id abc123def4 \
  --stage-name prod \
  --patch-operations op=replace,path=/throttling/burstLimit,value=1000,op=replace,path=/throttling/rateLimit,value=500

Stage Variables and Environment Issues

If your integration uses stage variables:

bash
aws apigateway get-stage \
  --rest-api-id abc123def4 \
  --stage-name prod \
  --query 'variables'

Update stage variables:

bash
aws apigateway update-stage \
  --rest-api-id abc123def4 \
  --stage-name prod \
  --patch-operations op=replace,path=/variables/apiEndpoint,value=https://api.example.com

CORS Issues (Manifesting as 500)

Sometimes CORS errors get reported as 500:

bash
aws apigateway get-method-response \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method OPTIONS \
  --status-code 200

Add CORS headers:

bash
aws apigateway update-method-response \
  --rest-api-id abc123def4 \
  --resource-id xyz123 \
  --http-method OPTIONS \
  --status-code 200 \
  --patch-operations \
    op=replace,path=/responseParameters/method.response.header.Access-Control-Allow-Origin,value="'*'" \
    op=replace,path=/responseParameters/method.response.header.Access-Control-Allow-Methods,value="'GET,POST,OPTIONS'" \
    op=replace,path=/responseParameters/method.response.header.Access-Control-Allow-Headers,value="'Content-Type,Authorization'"

Verification Steps

After making changes, test your API:

```bash # Test with API Gateway test-invoke-method aws apigateway test-invoke-method \ --rest-api-id abc123def4 \ --resource-id xyz123 \ --http-method GET \ --query '[status,body]'

# Make actual API call curl -v https://abc123def4.execute-api.us-east-1.amazonaws.com/prod/my-endpoint

# Monitor error rate aws cloudwatch get-metric-statistics \ --namespace AWS/ApiGateway \ --metric-name 5XXError \ --dimensions Name=ApiName,Value=my-api Name=Stage,Value=prod \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 300 \ --statistics Sum ```

Set up an alarm for 5XX errors:

bash
aws cloudwatch put-metric-alarm \
  --alarm-name api-gateway-5xx-errors \
  --alarm-description "API Gateway 5XX errors" \
  --namespace AWS/ApiGateway \
  --metric-name 5XXError \
  --dimensions Name=ApiName,Value=my-api Name=Stage,Value=prod \
  --statistic Sum \
  --period 60 \
  --threshold 5 \
  --comparison-operator GreaterThanOrEqualToThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:us-east-1:123456789012:alerts

Create a diagnostic script:

```bash #!/bin/bash API_ID="abc123def4" STAGE="prod" REGION="us-east-1"

echo "Checking API Gateway health..."

echo "1. Checking recent 5XX errors..." aws cloudwatch get-metric-statistics \ --namespace AWS/ApiGateway \ --metric-name 5XXError \ --dimensions Name=ApiName,Value=my-api Name=Stage,Value=$STAGE \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 300 \ --statistics Sum

echo "2. Testing endpoint..." curl -s -o /dev/null -w "HTTP Status: %{http_code}\n" \ https://$API_ID.execute-api.$REGION.amazonaws.com/$STAGE/health

echo "3. Checking Lambda errors (if applicable)..." aws cloudwatch get-metric-statistics \ --namespace AWS/Lambda \ --metric-name Errors \ --dimensions Name=FunctionName,Value=my-function \ --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ) \ --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \ --period 300 \ --statistics Sum

echo "Diagnostic complete." ```