Fix EventBridge Rule Never Fires in AWS: Event Pattern Matching, Sandbox Testing & Debugging

Logeshwaran.C
—

If your EventBridge rule never fires and your SQS queue stays empty, the fix is almost never the JSON pattern itself — it is one of seven specific, checkable things: a character-exact mismatch nobody notices by eye, content-filtering syntax that's subtly wrong, a rule listening on the wrong event bus, a missing resource-based permission on the queue, a missing KMS key statement on an encrypted queue, a FIFO queue target with no MessageGroupId, or a cross-account permission nobody wired up. Start by checking three CloudWatch metrics — TriggeredRules, Invocations, and FailedInvocations — because which of those three moves (or doesn't) tells you which of the seven you're chasing before you touch a single line of JSON.

⚡ Quick Answer

• Check CloudWatch first → look at TriggeredRules, Invocations, FailedInvocations for your rule.

• Nothing at all moves → your event pattern isn't matching, or the event never reached that bus. Jump to the pattern-matching section.

• Invocations and FailedInvocations both climb → the rule is firing but the queue is rejecting the message. Jump to the permissions section.

One line if you only read this box: test your pattern in the EventBridge Sandbox before you ever attach it to a rule — it will tell you the truth in ten seconds instead of an hour of guessing.

Jake called on a Tuesday, and he was doing that thing where he explains a problem by describing the exact moment he noticed it, instead of the problem itself. "My repair-ticket system stopped texting customers," he said. "I built this whole thing on Lambda and SQS last year, and Ethan wired an EventBridge rule to it so that every time a ticket status changes, it drops a message in the queue and a worker sends the text. Except now it doesn't. No errors. No messages. The queue's just... quiet."

That quiet is the whole problem with EventBridge. When something breaks in most systems, you get a stack trace, a red banner, a 500 error. EventBridge doesn't work that way. If a rule doesn't match, nothing happens — there's no exception, no log line that says "I ignored your event," just an event bus that received something and moved on as if nothing was even sent. That silence is what sends people down the wrong path for hours, rewriting a JSON pattern that was already correct while the real problem sits three steps downstream in a queue policy nobody's looked at since the day it was created.

"So it's an event pattern, right?" Jake said. "I stared at mine for twenty minutes. It looks exactly like the sample event."

"That's the part almost nobody expects," Ethan said. "AWS's own documentation states plainly that EventBridge matching is exact — character-by-character, with no case-folding and no normalization of any kind. Most AWS services will treat a colon and a slash in an Amazon Resource Name, or ARN — that's the unique ID string AWS gives every resource, like a mailing address for a specific queue or function — as interchangeable. EventBridge doesn't. If your pattern has one character different from the live event, in either direction, it silently fails to match. No warning. It just doesn't fire."

That's the shock worth sitting with before you open the console: two strings that look identical to a human eye, and are treated as identical by half of AWS, can be permanently different to EventBridge. That single design choice — exact match, no exceptions — is behind more "the rule just never fires" tickets than any actual bug in anyone's account.

Which of these seven is actually happening to you

"A rule never fires" is really seven different failures wearing the same symptom. Before you touch anything, figure out which lane you're in — it saves you from fixing the wrong layer.

What you observe Most likely cause Where to look
TriggeredRules metric is flat at zero Pattern doesn't match, or event never reached this bus Event pattern JSON, event bus name
Invocations rises, FailedInvocations rises too Rule fires; target rejects the call SQS resource policy, KMS key policy
Invocations rises, queue's NumberOfMessagesSent stays at zero Delivery accepted then blocked downstream, or wrong queue ARN in target Target configuration, queue ARN
Worked for weeks, then stopped with no code change A field the pattern depends on changed shape, or a key/permission expired Recent producer changes, key rotation, policy edits
Only worked after opening the rule in the console and saving it Infrastructure-as-code created the rule but not the target's resource policy CloudFormation/CDK/Terraform SQS policy resource
Works in one account, silent in the account the queue actually lives in Cross-account target permission missing on the queue side Queue's resource-based policy Principal block

‍♂️ Jake's Reality Check

"Can't I just... turn the rule off and back on? That fixes half my Wi-Fi problems."

No, and it's worth knowing why. A rule state of ENABLED or DISABLED only controls whether EventBridge evaluates events against it at all. It has nothing to do with whether the pattern matches, whether the target has permission, or whether a key policy is missing a line. Toggling it does exactly nothing to any of those three problems — it just wastes the five minutes you could've spent looking at a metric.

Start with the three CloudWatch metrics, not the JSON

CloudWatch is the monitoring service AWS built to collect metrics — numeric counters — from almost every other AWS service, including EventBridge, and graph them over time. EventBridge reports its own health under a namespace (a grouping label) called AWS/Events, and it sends new numbers to CloudWatch roughly once a minute. Three of those numbers do almost all the diagnostic work:

  1. TriggeredRules / MatchedEvents — how many events actually matched your rule's pattern. If this stays at zero while you know events are being sent, the pattern isn't matching, or the events are landing on a different event bus than the one your rule is attached to.
  2. Invocations — how many times EventBridge attempted to hand a matched event to your target. This number includes both successful and failed attempts, but AWS's own documentation notes it excludes attempts that are still being retried and haven't yet failed permanently. If TriggeredRules is climbing but Invocations stays flat, something is stopping the handoff before it's even attempted — usually a target that's been deleted or an ARN typo in the rule's target configuration.
  3. FailedInvocations — invocations that failed for good, with no more retries left. AWS's monitoring documentation is specific that this metric is only sent to CloudWatch if it's non-zero, so an absent graph line for FailedInvocations is itself informative: it means EventBridge has never reported a permanent failure for that rule, which usually rules out a permissions problem and points you back toward pattern matching.

There's a fourth number worth a glance if you set up a dead-letter queue (more on that later): DeadLetterInvocations, which AWS describes as the count of times a target wasn't invoked at all in response to an event — including cases that would have caused the same rule to fire itself again, creating an infinite loop.

Ethan's opinion here is blunt: "If you skip this step and go straight to rewriting your event pattern, you're debugging blind. These three metrics take ninety seconds to check and they immediately cut the search space in half. I don't rewrite a pattern until I know the pattern is actually the problem." Jake compared it, a little sheepishly, to the printer in his back office that everyone assumes is "just broken again" every Monday, when nine times out of ten it's simply out of toner — the light's been blinking the whole time, nobody looked.

Cause 1 — the event pattern doesn't actually match the event

An event pattern is a JSON object — a structured block of key-and-value pairs — that describes which incoming events a rule should react to. Per AWS's own documentation, a pattern either matches an event or it doesn't; there's no partial credit and no fuzzy matching. The rules that govern that yes-or-no decision are narrow and worth memorizing rather than guessing at:

  • Every field name in the pattern must exist in the event, in the same nested position. If your pattern checks detail.status but the real event nests it under detail.job.status, the pattern will never match — even if the value itself is correct.
  • Fields not mentioned in the pattern are ignored. AWS describes this as an implicit wildcard for anything you don't ask about, so a narrow, short pattern is not the problem — an incomplete one usually is.
  • Match values are always inside arrays, even when there's only one possible value. "source": "aws.ec2" is invalid; it has to be "source": ["aws.ec2"].
  • Matching is exact, character-by-character, with no case-folding. "Failed" and "failed" are different values as far as EventBridge is concerned.
  • Numbers are matched at the string-representation level. AWS's documentation gives the specific example that 300, 300.0, and 3.0e2 are not treated as equal, even though they're mathematically identical.
  • ARNs are matched exactly, colon-for-colon and slash-for-slash. This is the one that catches the most experienced engineers. AWS's own troubleshooting page states that most AWS services treat a colon (:) and a slash (/) interchangeably inside an ARN, but EventBridge does not — so an ARN copied from one context and pasted into a pattern with the separator style unchanged can fail to match a resource that is, functionally, the exact same resource.

✅ Why this is the one to check first

Pattern mismatches produce zero symptoms other than silence — no error, no log entry, nothing in FailedInvocations, because the rule was never triggered to begin with. Every other cause on this page at least leaves a trace somewhere in CloudWatch. This one doesn't, which is exactly why it has to be ruled out first, not last.

There's one more wrinkle specific to certain AWS-generated events: some events, including AWS API call events delivered through CloudTrail, simply don't populate the resources field at all, per AWS's documentation. If your pattern is built around matching a resource ARN and the source event type doesn't carry one, no amount of pattern tweaking will fix it — you need to match on a different field entirely, like detail.

How to test a pattern before you ever attach it to a rule

AWS ships a dedicated tool for exactly this problem, and a surprising number of people never open it. It's called the EventBridge Sandbox, and its entire purpose is to let you paste in a sample event and a candidate pattern and get a plain yes-or-no answer, without creating, editing, or risking a real rule.

‍♂️ Jake's Reality Check

"So I could've avoided this whole afternoon if I'd used the Sandbox first?"

Pretty much, yes. It exists precisely because AWS knows people paste patterns into a live rule, watch nothing happen, and then start second-guessing IAM permissions before they've confirmed the pattern itself is even right. Ethan compares it to checking a phone's SIM tray before assuming the whole handset is dead — it's the ten-second check people skip because it feels too obvious to be the answer.

  1. Open the EventBridge console and, in the navigation pane, go to Developer resources, then Sandbox, then the Event pattern tab.
  2. Choose a sample event. You can pick a real AWS service sample event, an EventBridge partner event, or paste in your own JSON under "Enter my own" — which is what you want if you're troubleshooting a custom application event rather than a native AWS service event.
  3. Pick a creation method for the pattern. "Custom pattern (JSON editor)" is the direct route if you already have a pattern written — paste it straight into the editor.
  4. Choose Test pattern. EventBridge displays a plain message stating whether the sample event matches the pattern you supplied — no ambiguity, no partial match.
  5. Once it matches, choose Create rule with pattern to carry that exact, confirmed-working pattern straight into a new rule, skipping the risk of a typo creeping in during copy-paste.

The same check is available outside the console, too, if your workflow is entirely code-driven: the TestEventPattern API action (or the test-event-pattern AWS CLI command) does the exact same comparison programmatically, which makes it easy to bolt into a CI pipeline so a pattern that stops matching a schema change gets caught before it ships, not after a customer notices their text message never arrived.

Cause 2 — you reached for content filtering and the syntax is off

Basic patterns only let you say "this field equals one of these exact values." Content filtering is EventBridge's name for a set of operators that let a pattern match ranges, prefixes, negations, existence checks, and wildcards instead of exact strings. It's powerful, and it's also the single most common place people write JSON that looks plausible but is structured just wrong enough to never match anything.

Operator What it does Example
prefix Matches string values starting with a given text "time": [{"prefix":"2026-09"}]
anything-but Matches any value except the one(s) listed "state":[{"anything-but":"initializing"}]
numeric Matches a numeric range or comparison "count":[{"numeric":[">",0,"<=",5]}]
exists Matches whether a field is present at all "ProductName":[{"exists":false}]
wildcard Matches a string against a pattern using the * character "FileName":[{"wildcard":"dir/*.png"}]
$or Matches if any one of several field conditions is true "$or":[{"Location":["NY"]},{"Day":["Mon"]}]

A handful of details in AWS's documentation trip people up specifically here:

  • anything-but with a prefix or suffix only accepts a single value, never a list. AWS's own docs flag this as a note directly under the operator's description — {"anything-but":{"prefix":"init"}} is valid, but a plain anything-but happily accepts a list of plain strings or numbers where the prefix and suffix variants do not.
  • Numeric matching has real precision limits. AWS documents it as working only for values between -5.0e9 and +5.0e9, with either 15 digits of precision or up to six digits to the right of the decimal point. A value outside that range simply won't be evaluated as expected.
  • Wildcards can't be stacked back-to-back. AWS's content-filtering documentation states you can use any number of wildcard characters in a value, but consecutive wildcard characters aren't supported — and if you actually need to match a literal asterisk or backslash character, you escape it with a backslash, since EventBridge otherwise treats * as a wildcard token, not a literal character. AWS also notes wildcard matching is currently supported in event bus rules specifically.
  • If you repeat the same key twice in one pattern, only the last one counts. AWS's documentation is explicit that when a key appears more than once in a pattern, the last reference is the one used to evaluate the event — the earlier one is silently discarded, not combined with it.
  • $or is field-level, not free-form boolean logic. EventBridge's declarative pattern language doesn't support AND/OR nested arbitrarily inside a single field's array of values — an array of values for one key is always evaluated as OR already, and $or exists specifically to let that same OR relationship span multiple different field names.

⚠️ What this actually breaks

A content-filtering pattern with a structural mistake — like duplicate keys, or a list where a single value is required — is still valid enough JSON that EventBridge accepts it when you save the rule. It doesn't reject the rule outright; it just quietly never matches, or matches differently than you intended. There's no validation step that catches "syntactically legal but semantically wrong" — that's what the Sandbox is for.

Cause 3 — the rule is listening on the wrong event bus

An event bus is the pipeline events flow through before rules ever get a chance to look at them, and an AWS account can have more than one. Every account has a default event bus, where native AWS service events (like an EC2 state change) land automatically, and an account can also have up to 100 custom event buses per Region, per AWS's published quotas — created specifically to receive events your own applications publish using the PutEvents API.

Here's the part that produces the exact symptom in this post's title: a rule is only ever evaluated against events arriving on the specific event bus it's attached to. If your application publishes custom events to a custom bus named, say, ticket-events, but the rule was created against the account's default bus — which is easy to do by accident, since the default bus is the default choice in the console and CLI alike — the rule will never see those events at all. Not "mismatch." Not "delayed." Never evaluated, ever, because they never arrive on the bus it's watching. CloudWatch's own metric documentation reflects this split directly: metrics that report only the RuleName dimension refer to the default event bus, while metrics reporting both EventBusName and RuleName dimensions refer to a named custom bus — so if you're checking TriggeredRules under the wrong bus's dimension, you'll see a flat zero even while the correct bus is matching events just fine.

"This one's sneaky because it feels like a pattern bug," Ethan said. "You'll swear the JSON is right — because it is right — and you'll never think to check which bus the rule is even sitting on, because in the console it's just one dropdown you clicked past on day one. It's like Jake's shop having two mail slots on the same door, one for the front counter and one for the back office — the letter arrives, it's just in the wrong pile, and nobody at the back office knows to go check the front slot."

Cause 4 — the rule fires but SQS never gets the message

If TriggeredRules is climbing and your queue is still empty, the pattern was never the problem — this is now purely a permissions question. Different EventBridge target types are authorized in different ways, and mixing them up is its own quiet source of confusion:

Target type How EventBridge is authorized Key detail
Amazon SQS Resource-based policy on the queue, or an IAM execution role If no role is configured, EventBridge falls back to the queue's own resource-based policy
AWS Lambda Resource-based policy on the function, or an IAM execution role Console auto-fixes it when you re-add the target; APIs and IaC do not
Amazon SNS Resource-based policy on the topic, or an IAM execution role A missing or stale topic policy is the single most common SNS-target failure
Amazon CloudWatch Logs Resource-based policy only AWS's documentation says explicitly: do not specify a RoleArn for this target type
Amazon Kinesis streams Identity-based policy (IAM execution role) only There's no resource-based fallback here, unlike Lambda, SNS, or SQS

AWS's own knowledge center walks through the exact same diagnostic order this section is built around for the SQS case specifically: check TriggeredRules, Invocations, and FailedInvocations first; if there's data for both Invocations and FailedInvocations, that combination typically points to a missing IAM permission, and the fix is to confirm the queue's resource-based policy explicitly names events.amazonaws.com as the principal and sqs:SendMessage as the allowed action. AWS also recommends checking the queue's own NumberOfMessagesSent metric directly on the SQS side, as independent confirmation of whether anything actually landed, separate from what EventBridge thinks happened.

A minimal resource-based policy statement that grants this, adapted from AWS's own cross-account permissions documentation, looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowEventBridgeSendMessage",
      "Effect": "Allow",
      "Principal": { "Service": "events.amazonaws.com" },
      "Action": "sqs:SendMessage",
      "Resource": "arn:aws:sqs:us-east-1:123456789012:MyQueue"
    }
  ]
}

✅ Why this is the one to fix even if you use an execution role too

Since EventBridge only falls back to the resource-based policy when no execution role is configured, some engineers assume attaching a role is enough on its own. AWS's IAM troubleshooting guidance also notes that the role's trust policy has to explicitly allow events.amazonaws.com to assume it, and the permission policy attached to that role separately needs the target action — sqs:SendMessage for a queue, the same way lambda:InvokeFunction is needed for a function. Two documents, both need the right statement, and it's easy to get one right and forget the other.

Cause 5 — the queue is encrypted and the key policy is missing a line

SQS supports server-side encryption using AWS Key Management Service (KMS) — a service that manages encryption keys so message contents are encrypted at rest inside the queue. When a queue uses a customer-managed KMS key (a key you created and control, as opposed to the AWS-managed default), AWS's troubleshooting documentation is direct about what happens if the key's own policy doesn't separately authorize EventBridge: events are simply not delivered to the target queue. The SQS resource policy alone isn't enough in that case — you need this statement added to the KMS key's policy as well:

{
  "Sid": "Allow EventBridge to use the key",
  "Effect": "Allow",
  "Principal": { "Service": "events.amazonaws.com" },
  "Action": [ "kms:Decrypt", "kms:GenerateDataKey" ],
  "Resource": "*"
}

This is easy to miss precisely because it's a second, separate policy document living on a different resource entirely — the key, not the queue — and nothing in the EventBridge console flags it as missing when you set up the target. It just fails quietly, exactly the same way an unpermissioned queue does, which is why it belongs on the same checklist rather than a separate one.

Cause 6 — the target is a FIFO (or fair) queue missing a MessageGroupId

A FIFO queue — First-In-First-Out — guarantees messages are processed in the exact order they were sent, and requires every message to carry a MessageGroupId, a label AWS uses to keep related messages in strict order relative to each other. When EventBridge targets an SQS queue, this ID isn't inferred automatically; AWS's CloudFormation reference for the rule's SQS target parameters requires it be supplied explicitly as part of the target's SqsParameters configuration whenever the destination queue is FIFO. Leave it out on a FIFO target and the delivery attempt fails outright, which shows up as a rise in Invocations paired with a rise in FailedInvocations — the exact same symptom as a missing resource policy, just with a completely different fix.

 What changed between versions

  • Before: a MessageGroupId was only ever relevant for FIFO queue targets, since standard queues had no concept of ordering groups.
  • Now: AWS's own SDK documentation for the SQS target parameters describes MessageGroupId as still required for FIFO queues, but now also optionally usable on standard queues in support of the newer SQS fair queues capability, which groups related messages for fairer processing without requiring full FIFO ordering.
  • What that means for you: don't assume "standard queue" means this field is always irrelevant to check — confirm which queue type you actually have before ruling this cause out.

Cause 7 — the rule was deployed by CloudFormation, CDK, or Terraform and never got its permissions

This one is worth calling out on its own because it produces one of the most confusing patterns in this entire troubleshooting flow: a rule that works fine the moment you open it in the console and click Save, but never worked when it was first deployed. AWS's own troubleshooting documentation explains the mechanism behind this, in the context of Lambda permissions specifically, and the same logic applies to SQS and SNS: if a target's resource-based policy is missing or incorrect, editing the rule in the EventBridge console — removing the target and adding it back — causes the console to automatically set the correct permissions on the target for you.

The console does this for you invisibly. Infrastructure-as-code tools generally don't, unless you write the permission statement into the template yourself. If your rule and its target queue are both defined in CloudFormation, CDK, or Terraform, and you never separately declared an AWS::SQS::QueuePolicy (or its equivalent) granting events.amazonaws.com access, the rule can deploy successfully, show as ENABLED, and still never deliver a single message — because nothing ever told the queue it was allowed to accept mail from EventBridge in the first place.

‍♂️ Jake's Reality Check

"So the console is basically doing me a favor I didn't know about, and my Terraform script isn't?"

Correct. Nothing is wrong with your Terraform — it's doing exactly what you told it to do. The console just quietly does one extra thing on your behalf that code-based deployment tools leave entirely up to you to write explicitly.

Cross-account and partner-bus edge cases

Two setups deserve their own callout because the failure mode looks identical to a simple permissions miss, but the actual missing piece is different.

Cross-account targets. When a rule in one AWS account needs to send events to an SQS queue that lives in a different account, AWS's documentation describes attaching a resource access policy on the queue, in the target account, that names the calling account as the allowed principal — for example, granting SQS:SendMessage to a specific account ID rather than to events.amazonaws.com alone. AWS's documentation also flags an important constraint here: calling PutTarget from a different account than the event bus itself — even while supplying an execution role from the calling account — is not supported for cross-account targets other than event buses. In practice, that means the rule and its target definition both have to be set up from the account that owns the event bus, with the receiving account's queue granting access back to it, not the other way around.

Partner event buses. Events from third-party SaaS platforms that integrate with EventBridge (AWS's documentation gives Salesforce as an example) arrive on a partner event bus rather than your account's default or custom bus, and a rule has to be created against that specific partner bus to ever see them. A pattern that's otherwise perfect, sitting on a rule attached to the default bus, will never match a single partner event, for the same underlying reason a custom-bus event never reaches a default-bus rule: the event and the rule simply never occupy the same pipeline.

Why a rule that worked yesterday can stop matching today

Rules that ran fine for weeks and then went silent overnight, with nobody touching the rule itself, almost always trace back to something changing on one of the other two ends of the pipeline: the event producer, or a shared credential.

  • The producer changed its event shape. A field your pattern depends on got renamed, moved into a nested object, or started sending a different casing — any of which is a silent, permanent mismatch under the exact-match rules covered earlier.
  • A recent rule or target edit hasn't taken effect yet. AWS's troubleshooting documentation is explicit that changes to a rule or its targets don't apply to in-flight events immediately — there's a short propagation delay, and testing right after a save can produce a false negative that resolves itself within a minute or two.
  • You're matching a global-service event outside its one valid Region. AWS's documentation notes that global services — IAM and Route 53 among them — only emit API-call events in the US East (N. Virginia) Region, regardless of which Region you actually operate in. A rule built anywhere else to catch those events will never see them.

What happens when the queue can't keep up

EventBridge doesn't give up on a delivery after a single try. AWS's documentation states it attempts delivery to a target for up to 24 hours, retrying automatically if the target service is having trouble, before finally giving up and recording it as a permanent failure. Separately, if a target is throttling incoming requests for a prolonged period — because it's not provisioned to absorb the traffic EventBridge is sending on your behalf — AWS notes that EventBridge might stop retrying delivery for that event altogether rather than holding onto it indefinitely.

This matters for burst-heavy workloads specifically. If Jake's shop runs a promotion and ticket volume spikes tenfold in an hour, and the downstream Lambda consuming from SQS is scaled conservatively, the queue itself will happily absorb the burst — that's the entire point of using a queue as a buffer — but if the rule's delivery attempts to SQS are throttled at the EventBridge-to-target layer rather than at SQS itself, some fraction of events during that spike can be the ones that quietly age out of the 24-hour retry window with nowhere else to go.

Should you add a dead-letter queue

A dead-letter queue, or DLQ, is a separate SQS queue where EventBridge places events it permanently failed to deliver, instead of just dropping them. AWS's own troubleshooting documentation specifically recommends setting one up so that failed deliveries are preserved somewhere you can inspect and replay them, rather than vanishing the moment the 24-hour retry window closes.

⚠️ What skipping a DLQ actually breaks

Without one, every permanently failed delivery — whether it's a permissions problem you haven't caught yet, or a target that was briefly throttled during a burst — disappears with no record. You won't even know how many customer notifications you lost until a customer tells you.

If you're specifying a DLQ through the API rather than the console, or pointing it at a queue in a different AWS account, AWS's documentation is specific that you must manually attach a resource-based policy granting EventBridge sqs:SendMessage permission on that DLQ too — the same category of permission this whole post has been circling back to, just on a second queue.

Keeping this from happening again

A few habits, drawn straight from the causes above, prevent almost every repeat of this problem:

  1. Test every new or edited pattern in the Sandbox, or with TestEventPattern, before it ever touches a real rule. It takes less time than writing the pattern did.
  2. Write the target's resource-based policy explicitly in your infrastructure code, rather than relying on a console action nobody will remember to repeat the next time the stack is redeployed from scratch.
  3. Set a CloudWatch alarm on FailedInvocations. AWS's own troubleshooting documentation includes step-by-step instructions for exactly this: alarm on the Sum statistic being greater than or equal to 1 for a sustained period, so a permissions regression gets caught in minutes instead of when a customer complains.
  4. Attach a dead-letter queue to every production rule, so failed deliveries are recoverable rather than gone.
  5. Keep event patterns under the 2,048-character quota AWS publishes for pattern size (adjustable to 4,096 through a support request) — a pattern that's grown unwieldy with dozens of anything-but exclusions is a sign it's time to simplify the event shape upstream instead.

Jake's fix, in the end, was almost boring: his rule's SQS target queue was encrypted with a customer-managed key created a few months after the original rule, and nobody had gone back to add the EventBridge statement to that key's policy. "It genuinely wasn't glamorous," Ethan said. "It never is. That's kind of the whole lesson here."

‍♂️ Jake's Reality Check

"What if I check all of this and it's still nothing? What if I genuinely don't know if my queue is encrypted?"

Then look, don't guess. We can't see into your account from here, and no generic checklist can tell you what's actually configured on your specific queue. Open the SQS console, click your queue, and check the Encryption section directly — that single page will tell you in seconds whether a KMS key is even in play for cause five to apply to you at all.

Frequently asked questions

My EventBridge rule shows "Enabled" — why does it still never fire?

The ENABLED state only controls whether EventBridge evaluates incoming events against the rule's pattern at all — it says nothing about whether the pattern matches, whether the target has permission to accept the event, or whether the events are even arriving on the bus the rule is attached to. A rule can be perfectly enabled and still never fire for any of the reasons covered above.

How can I test an event pattern without creating a real rule?

Use the EventBridge Sandbox in the console, under Developer resources, or the TestEventPattern API action (test-event-pattern in the AWS CLI). Both accept a sample event and a candidate pattern and return a direct yes-or-no on whether they match, with no rule ever created.

Why doesn't my pattern match even though it looks identical to the event JSON?

The most common causes are a nesting mismatch (the pattern checks a field at the wrong depth in the JSON), a character that differs in an ARN's colon-versus-slash usage, or a value that differs only in case. EventBridge's matching is exact and case-sensitive by design, so visual similarity isn't the same as a match.

Is EventBridge pattern matching case-insensitive?

No. AWS's documentation states the matching is exact, character-by-character, without case-folding or any other string normalization. If you specifically need case-insensitive matching for prefix, suffix, or anything-but operators, you can combine any of those with the separate equals-ignore-case option, but plain string matching always remains case-sensitive.

Why did my rule stop matching after I copied an ARN from the console?

Most AWS services treat a colon and a slash inside an ARN as interchangeable, so copying an ARN between two contexts that format it slightly differently usually causes no issue anywhere else in AWS. EventBridge is the exception — its pattern matching treats those characters as distinct, so an ARN that's functionally the same resource but formatted with a different separator will fail to match.

What's the difference between "anything-but" and "exists": false?

"exists": false checks whether a field is present in the event at all, regardless of its value. anything-but checks that a field is present but its value is not one of the listed values — it requires the field to exist with a different value, rather than being absent entirely.

Can I match "this OR that" across two different fields?

Yes, using the $or operator, which AWS's content-filtering documentation describes as a way to build event patterns that check whether any value across multiple different fields matches, rather than being limited to OR logic within a single field's own array of values.

Why does numeric matching reject a number that looks correct?

AWS's numeric matching only operates on values between -5.0e9 and +5.0e9, with 15 digits of precision or up to six digits after the decimal point. A number outside that range, or with more precision than that, won't be evaluated the way you expect. Separately, remember that number matching happens at the string-representation level, so 300, 300.0, and 3.0e2 are treated as different values by plain equality matching even though they're the same number.

My rule's TriggeredRules count is going up, so why is my SQS queue empty?

If the rule is confirmed to be matching (TriggeredRules climbing) but the queue never receives anything, the problem has moved downstream of the pattern entirely. Check Invocations and FailedInvocations next: if both are climbing, it's almost always a missing resource-based policy statement on the queue, or a missing KMS key policy statement if the queue is encrypted.

Do I need to touch KMS permissions for an encrypted SQS queue?

Yes, if the queue uses a customer-managed KMS key. AWS's troubleshooting documentation states you must add a statement to that key's policy allowing the events.amazonaws.com principal to use kms:Decrypt and kms:GenerateDataKey — the queue's own resource-based policy alone isn't sufficient when a customer-managed key is involved.

Why did my rule only start delivering after I opened and saved it in the console?

AWS's documentation notes that editing a rule's target in the console — removing it and adding it back — causes the console to automatically set the correct resource-based permissions on that target. Rules created purely through the CLI, an SDK, or infrastructure-as-code tools like CloudFormation, CDK, or Terraform don't get this automatic step unless the permission is written into the deployment explicitly.

Does a FIFO SQS target need a MessageGroupId?

Yes. AWS's CloudFormation and target-parameter documentation both specify that a MessageGroupId is required when the target queue is FIFO, and it's supplied through the rule's SqsParameters configuration. A missing MessageGroupId on a FIFO target causes delivery to fail. It's also now optionally usable on standard queues to support SQS's newer fair-queues feature, so don't assume standard queues never need it.

Why did my rule work yesterday and stop matching today with no changes?

Check whether the event producer changed the shape or casing of a field your pattern depends on — that's the most common cause of a rule breaking with no changes on the EventBridge side at all. Also confirm nothing shifted on a shared credential, like a KMS key rotation, if the target queue is encrypted.

What happens to events if my SQS queue is throttled or full during a burst?

EventBridge retries delivery to a struggling target for up to 24 hours before recording a permanent failure, per AWS's documentation. However, AWS also notes that if a target stays constrained for a prolonged period, EventBridge might stop retrying delivery for that event rather than holding it indefinitely — which is exactly the scenario a dead-letter queue is meant to protect against.

Should every EventBridge rule have a dead-letter queue?

AWS's own troubleshooting guidance recommends setting one up specifically so that events which fail permanent delivery are preserved and can be inspected or replayed, rather than being lost entirely once the 24-hour retry window ends. For any rule feeding something customer-facing, like a notification pipeline, it's a small amount of setup for a meaningful safety net.

If two rules both match the same event, does the second one block the first?

No — EventBridge evaluates every enabled rule on a given event bus independently against each incoming event. A single event can match any number of rules simultaneously, and each matching rule fires its own targets without any interaction between rules. If you're only seeing one of two rules fire, that points back to the same causes covered throughout this post — pattern mismatch, wrong bus, or a permissions gap on that specific rule's target — not to any kind of rule-versus-rule precedence.

Revision note. Written September 2026. if a specific limit or console label has since moved, treat the underlying troubleshooting order in this post — metrics first, pattern second, permissions third — as the part that holds up regardless. If you've been staring at a silent queue for an hour, we hope the ninety seconds it takes to check those three metrics gets you your answer faster than it got Jake his.

Related