Home / AWS

AWSTroubleshootingIntermediate

EventBridge rule not triggering its target: patterns, permissions and FailedInvocations

An EventBridge rule exists but the Lambda, SQS queue or Step Functions target never runs. Work out whether the event never arrived, didn't match, or failed delivery, then fix it.

Deepak Bhardwaj7 min read

Applies toAmazon EventBridge (event buses and rules), targets including Lambda, SQS, SNS, Step Functions and CloudWatch Logs

Symptom

You created an EventBridge rule, the source system is doing its thing, and the target never fires. No Lambda invocation, no message in the queue, no Step Functions execution. The console shows the rule as Enabled and gives no error.

Every case falls into one of three buckets, and the rule's CloudWatch metrics tell you which one in under a minute:

What the metrics show Meaning Where to look
MatchedEvents / TriggeredRules = 0 Event never arrived, or the pattern doesn't match Source, Region, bus, pattern
TriggeredRules > 0, FailedInvocations > 0 Matched, but delivery to the target failed Target permissions, KMS, target existence
Invocations > 0, FailedInvocations = 0 EventBridge delivered successfully The target itself (Lambda errors, consumer logic)

Check 1: read the rule metrics

Metrics are in the AWS/Events namespace with a RuleName dimension (rules on a custom bus also carry EventBusName).

RULE=orders-created-to-lambda

for M in TriggeredRules Invocations FailedInvocations ThrottledRules; do
  echo -n "$M: "
  aws cloudwatch get-metric-statistics --namespace AWS/Events --metric-name $M \
    --dimensions Name=RuleName,Value=$RULE \
    --start-time $(date -u -d '-3 hours' +%Y-%m-%dT%H:%M:%SZ) \
    --end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
    --period 3600 --statistics Sum \
    --query "sum(Datapoints[].Sum)" --output text
done

If the rule is on a custom bus, add Name=EventBusName,Value=<bus> to --dimensions. An empty result means no datapoints, which is the same as zero.

Check 2: confirm the rule, the bus and the Region

aws events describe-rule --name $RULE --event-bus-name default \
  --query "{state:State,bus:EventBusName,pattern:EventPattern,schedule:ScheduleExpression}"

aws events list-targets-by-rule --rule $RULE --event-bus-name default \
  --query "Targets[].{id:Id,arn:Arn,role:RoleArn,dlq:DeadLetterConfig.Arn}" --output table

Things that catch people out:

  • Wrong bus. AWS service events go to the default bus. A rule on a custom bus only sees events that are sent to that bus with put-events --entries EventBusName=....
  • Wrong Region. Rules only see events in their own Region. Global services such as IAM emit their CloudTrail-based events in us-east-1, so a rule watching for CreateUser in eu-west-1 never fires.
  • State is DISABLED. Re-enable with aws events enable-rule --name $RULE.
  • Schedules are UTC. cron(0 9 * * ? *) runs at 09:00 UTC, not local time. For time-zone-aware schedules use EventBridge Scheduler instead of a scheduled rule.

Check 3: test the pattern against a real event

Don't eyeball patterns. Grab a real event (from the source service's docs, a CloudTrail record, or a CloudWatch Logs target as in Check 5) and test it:

aws events test-event-pattern \
  --event-pattern file://pattern.json \
  --event file://sample-event.json

It returns {"Result": true} or false. The sample event must include the envelope fields (id, account, source, time, region, resources, detail-type, detail), otherwise the call errors.

Common pattern mistakes:

Mistake Wrong Right
Value not in an array "source": "aws.ec2" "source": ["aws.ec2"]
Wrong key name "detailType" "detail-type"
Case mismatch ["running"] vs event "Running" Match exact case, or use equals-ignore-case
Number quoted "size": ["100"] "size": [100] or a numeric filter
Field path wrong "detail": {"state": ...} when event has detail.state.name Mirror the exact nesting of the event
Prefix on a non-string prefix on a number Use numeric matching

Source-specific prerequisites

  • S3 object events are only sent to EventBridge once you turn it on for the bucket. Note that this call replaces the bucket's whole notification configuration, so include any existing SNS/SQS/Lambda notifications in the JSON:
aws s3api get-bucket-notification-configuration --bucket my-bucket
aws s3api put-bucket-notification-configuration --bucket my-bucket \
  --notification-configuration '{"EventBridgeConfiguration": {}}'
  • "AWS API Call via CloudTrail" events need CloudTrail logging the management events. Read-only calls (Get*, List*, Describe*) are not delivered unless the rule's state is ENABLED_WITH_ALL_CLOUDTRAIL_MANAGEMENT_EVENTS.
  • Custom applications must send exactly the Source and DetailType your pattern expects. Send a test event yourself:
aws events put-events --entries '[{
  "Source": "com.example.orders",
  "DetailType": "OrderCreated",
  "Detail": "{\"orderId\":\"123\",\"total\":42}",
  "EventBusName": "orders-bus"
}]'

Check FailedEntryCount in the response. A non-zero count means the bus rejected the event, usually because of a missing events:PutEvents permission or a malformed Detail string.

Check 4: fix FailedInvocations (delivery permissions)

When FailedInvocations is climbing, EventBridge matched the event but the target refused it. How EventBridge gets permission depends on the target type:

Target Permission model Usual fix
Lambda Function resource policy lambda add-permission for events.amazonaws.com
SQS, SNS Queue / topic policy Allow events.amazonaws.com with aws:SourceArn = rule ARN
CloudWatch Logs Logs resource policy aws logs put-resource-policy (the console does this for you)
Step Functions, Kinesis, ECS, API destinations, other buses IAM role on the target (RoleArn) Role trusts events.amazonaws.com and allows the target action

Lambda resource policy:

RULE_ARN=$(aws events describe-rule --name $RULE --query Arn --output text)

aws lambda get-policy --function-name process-orders --query Policy --output text

aws lambda add-permission --function-name process-orders \
  --statement-id eventbridge-$RULE \
  --action lambda:InvokeFunction \
  --principal events.amazonaws.com \
  --source-arn $RULE_ARN

If you recreated the rule, or it lives on a custom bus, the ARN changed (rule/<bus-name>/<rule-name> for custom buses) and an old SourceArn condition no longer matches.

SQS queue policy statement:

{
  "Sid": "AllowEventBridge",
  "Effect": "Allow",
  "Principal": {"Service": "events.amazonaws.com"},
  "Action": "sqs:SendMessage",
  "Resource": "arn:aws:sqs:eu-west-1:111122223333:orders-queue",
  "Condition": {"ArnEquals": {"aws:SourceArn": "arn:aws:events:eu-west-1:111122223333:rule/orders-created-to-sqs"}}
}

Warning

An SQS queue or SNS topic encrypted with the AWS managed key (aws/sqs, aws/sns) cannot receive events from EventBridge, because you can't edit that key's policy. Switch the queue to a customer managed KMS key whose policy allows events.amazonaws.com to use kms:GenerateDataKey and kms:Decrypt.

Add a dead-letter queue so failures explain themselves

Without a DLQ, failed events are dropped after retries and you only see a counter. With one, each failed event lands in SQS with ERROR_CODE, ERROR_MESSAGE, RULE_ARN and TARGET_ARN message attributes.

aws events put-targets --rule $RULE --targets '[{
  "Id": "process-orders",
  "Arn": "arn:aws:lambda:eu-west-1:111122223333:function:process-orders",
  "RetryPolicy": {"MaximumRetryAttempts": 8, "MaximumEventAgeInSeconds": 3600},
  "DeadLetterConfig": {"Arn": "arn:aws:sqs:eu-west-1:111122223333:eventbridge-dlq"}
}]'

aws sqs receive-message --queue-url https://sqs.eu-west-1.amazonaws.com/111122223333/eventbridge-dlq \
  --message-attribute-names All --max-number-of-messages 5 \
  --query "Messages[].MessageAttributes.{code:ERROR_CODE.StringValue,msg:ERROR_MESSAGE.StringValue}"

The DLQ itself needs a queue policy allowing events.amazonaws.com to send to it.

Caution

put-targets with an existing target Id replaces that target's whole configuration, including input transformers and role. Run list-targets-by-rule first and include every field you want to keep.

Check 5: see what actually matched

Add a CloudWatch Logs log group as a second target on the same rule. Every matched event gets written there in full, which proves matching works and gives you real sample events for test-event-pattern. Create it from the console (log group name under /aws/events/) so the resource policy is set up for you, and remove the target once you're done.

Invocations succeed but nothing happens

If Invocations rises and FailedInvocations stays at zero, EventBridge has done its job. For Lambda, EventBridge invokes asynchronously: a successful invocation only means Lambda accepted the event. Check the function itself:

aws logs tail /aws/lambda/process-orders --since 1h --filter-pattern ERROR

Also check the function's Errors and Throttles metrics, and whether an input transformer on the target is reshaping the event into something your code doesn't expect.

Prevent it next time

  • Alarm on FailedInvocations > 0 for every production rule, and on the DLQ's ApproximateNumberOfMessagesVisible.
  • Give every target a DLQ and an explicit retry policy.
  • Store patterns in code with a sample event next to each one, and run test-event-pattern in CI.
  • Define rules, targets and their resource policies in the same Terraform or CloudFormation stack, so recreating a rule also recreates the permission with the right ARN.
  • Use customer managed KMS keys for any encrypted SQS or SNS target.