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
defaultbus. A rule on a custom bus only sees events that are sent to that bus withput-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 forCreateUserineu-west-1never fires. - State is
DISABLED. Re-enable withaws 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 isENABLED_WITH_ALL_CLOUDTRAIL_MANAGEMENT_EVENTS. - Custom applications must send exactly the
SourceandDetailTypeyour 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 allowsevents.amazonaws.comto usekms:GenerateDataKeyandkms: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-targetswith an existing targetIdreplaces that target's whole configuration, including input transformers and role. Runlist-targets-by-rulefirst 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 > 0for every production rule, and on the DLQ'sApproximateNumberOfMessagesVisible. - 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-patternin 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.