S3 Event Notification System Trigger Failure: When the Lambda Never Fires (S3 → Lambda → DynamoDB)

 

S3 Event Notification System Trigger Failure: When the Lambda Never Fires (S3 → Lambda → DynamoDB)

When S3 Event Notification System fails, the rest of your architecture doesn’t even know.





Problem

Your pipeline looks clean on paper.

An object uploads to S3, a Lambda function processes it, and results are written to DynamoDB.

Everything appears normal—no console errors, no alarms, no red lights.

But your DynamoDB table hasn’t updated for hours. You run the metrics: zero invocations.

CloudWatch shows nothing. Lambda logs are empty.

Everything is “green,” yet nothing is happening.

Your S3 trigger has silently failed.


Clarifying the Issue

This failure doesn’t originate in Lambda or DynamoDB.

It starts within S3’s Event Notification System—the configuration that lives inside the S3 bucket itself.

That JSON object tells S3 which Lambda function to invoke and for what events.

When this configuration is invalid, overwritten, or missing permission to invoke Lambda, nothing downstream ever happens.

You can retrieve or inspect it using:

aws s3api get-bucket-notification-configuration --bucket trigger-demo-bucket

Output (showing an error):

{}

If it returns {}, your bucket has no active event mapping, and Lambda never receives a signal.

Because S3 doesn’t report these as “errors,” the system fails silently—and production workloads drift out of sync before anyone notices.


Why It Matters

Missed triggers equal lost business events.

Files that should have been processed remain untouched, customers wait, and analytics fall behind.

AWS doesn’t retry these indefinitely.

Once an S3 event fails to trigger Lambda, that moment is gone forever.

This is data loss disguised as success.


Key Terms

S3 Event Notification — The configuration JSON stored inside the S3 bucket that defines which Lambda function, SNS topic, or SQS queue receives event data.

Lambda Function — Stateless compute that responds to S3 events. It never stores or manages the trigger—it only receives events.

DynamoDB Table — The destination data store for processed results.

Dead Letter Queue (DLQ) — A safety net that stores failed Lambda invocations for later review.


Steps at a Glance

  1. Create the base AWS resources (S3, DynamoDB, Lambda).
  2. Configure and verify the S3 Event Notification System that lives in the bucket.
  3. Upload a test file to S3 and observe the flow.
  4. Diagnose the failure if the event doesn’t trigger Lambda.
  5. Apply hardened fixes for long-term resilience.

Detailed Steps


Step 1 – Create the Base Infrastructure

Create an S3 bucket and DynamoDB table:

aws s3 mb s3://trigger-demo-bucket

aws dynamodb create-table \
  --table-name trigger-demo-table \
  --attribute-definitions AttributeName=id,AttributeType=S \
  --key-schema AttributeName=id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

Write a minimal Lambda function:

import json, boto3
ddb = boto3.client('dynamodb')

def handler(event, context):
    record = event['Records'][0]
    key = record['s3']['object']['key']
    ddb.put_item(
        TableName='trigger-demo-table',
        Item={'id': {'S': key}, 'status': {'S': 'processed'}}
    )
    return {"statusCode": 200, "body": "OK"}

Deploy the function:

aws lambda create-function \
  --function-name s3-trigger-demo \
  --runtime python3.12 \
  --handler lambda_function.handler \
  --zip-file fileb://function.zip \
  --role arn:aws:iam::<account-id>:role/LambdaExecRole

Step 2 – Configure the S3 Event Notification System

This is where most trigger failures begin.

The configuration JSON lives in S3—not Lambda.

Attach the trigger:

aws s3api put-bucket-notification-configuration \
  --bucket trigger-demo-bucket \
  --notification-configuration '{
    "LambdaFunctionConfigurations": [
      {
        "LambdaFunctionArn": "arn:aws:lambda:us-east-1:<account-id>:function:s3-trigger-demo",
        "Events": ["s3:ObjectCreated:*"]
      }
    ]
  }'

Verify it’s active:

aws s3api get-bucket-notification-configuration --bucket trigger-demo-bucket

Expected output should list your Lambda function.

If the response is {} or incomplete, S3 has no trigger—and your pipeline will never start.

Output (showing an error):

{}

Step 3 – Upload and Observe

Upload a file to test:

aws s3 cp test.txt s3://trigger-demo-bucket/

Then check Lambda metrics:

aws lambda get-metric-data \
  --metric-data-queries '[
    {
      "Id": "invocations",
      "MetricStat": {
        "Metric": {
          "Namespace": "AWS/Lambda",
          "MetricName": "Invocations",
          "Dimensions": [{"Name": "FunctionName","Value":"s3-trigger-demo"}]
        },
        "Period":300,
        "Stat":"Sum"
      },
      "ReturnData":true
    }
  ]' \
  --start-time 2025-10-27T00:00:00Z \
  --end-time 2025-10-27T23:59:59Z

If the JSON response shows empty values, the trigger never fired:

{
  "MetricDataResults": [
    {"Id": "invocations", "Values": []}
  ]
}

Check DynamoDB for new entries:

aws dynamodb scan --table-name trigger-demo-table

If it returns:

{"Count": 0, "Items": []}

you’ve confirmed a trigger misfire.

Note: Even when properly configured, S3→Lambda invocations can take several seconds to appear in metrics due to asynchronous event delivery. Be patient before concluding the trigger has failed.


Step 4 – Diagnose the Failure

Common causes include:

Missing Permission – S3 needs explicit permission to invoke your Lambda.

Add it with:

aws lambda add-permission \
  --function-name s3-trigger-demo \
  --principal s3.amazonaws.com \
  --statement-id s3invoke \
  --action lambda:InvokeFunction \
  --source-arn arn:aws:s3:::trigger-demo-bucket

Region Mismatch – S3 and Lambda must be in the same region.

Notification Overwrite – A later put-bucket-notification-configuration call can erase previous triggers.

Always recheck configuration after redeployments.

IAM Gaps – Ensure the Lambda execution role includes:

{
  "Action": "dynamodb:PutItem",
  "Effect": "Allow",
  "Resource": "*"
}

For production systems, scope the Resource to the specific DynamoDB table ARN rather than using *, following the principle of least privilege.

Empty Logs – If CloudWatch shows nothing:

aws logs filter-log-events --log-group-name /aws/lambda/s3-trigger-demo

and returns no events, Lambda was never invoked.


Step 5 – Apply Hardened Fixes

Route Through EventBridge
Replace direct S3→Lambda triggers with EventBridge rules.

This adds retries, visibility, and metrics.

Add a Dead Letter Queue (DLQ)
Configure a backup queue for failed invocations:

aws lambda update-function-configuration \
  --function-name s3-trigger-demo \
  --dead-letter-config '{"TargetArn":"arn:aws:sqs:us-east-1:<acct-id>:lambda-dlq"}'

Emit Custom CloudWatch Metrics
Publish a heartbeat from within Lambda:

import boto3
cw = boto3.client('cloudwatch')
cw.put_metric_data(
    Namespace='S3TriggerHealth',
    MetricData=[{'MetricName':'EventProcessed','Value':1}]
)

If the metric stops incrementing, you’ll know the trigger broke.

Reconciliation Job
Schedule a nightly Lambda to:

  • List all S3 objects.
  • Cross-check DynamoDB.
  • Log any missing entries.

This closes the loop for good.


Pro Tips

  • Re-deploying Lambda changes its ARN. Always update your S3 notification configuration after redeploy.
  • Keep a version-controlled copy of your put-bucket-notification-configuration JSON.
  • Automate a post-deploy smoke test: upload a dummy file and confirm a new record appears in DynamoDB.

Conclusion

The S3→Lambda→DynamoDB chain is the heartbeat of modern AWS workloads.

When that S3 Event Notification System fails, the rest of your architecture doesn’t even know.

No alarms. No retries. Just quiet failure.

Treat your triggers like mission-critical hardware: test them, log them, and monitor them relentlessly.

Because in AWS, silence is the most dangerous symptom of all.


Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.

Comments

Popular posts from this blog

The New ChatGPT Reason Feature: What It Is and Why You Should Use It

Raspberry Pi Connect vs. RealVNC: A Comprehensive Comparison

Insight: The Great Minimal OS Showdown—DietPi vs Raspberry Pi OS Lite