BadEventFilterInvalid
BadEvent filters
BadEventFilterInvalid rejects the EventFilter as a whole rather than one operand. Usual causes are an empty select clause, a where clause whose operator and operand counts do not match, or every select clause failing at once. Start from a minimal BaseEventType select list with no where clause and build up.
What you actually see
An attempt to subscribe to events on the Server object or an alarm area fails immediately, and no events are ever delivered even though the server clearly raises them for other clients.
Why it happens
- Empty or entirely invalid select clauseAn EventFilter with no select clauses asks for events with no fields, which is not useful and is refused. The same happens when every select clause is individually invalid - the filter has nothing left that works, so the whole filter is rejected rather than each clause separately.
- Where clause operator and operand arity mismatchEach ContentFilterElement has an operator and an operand array whose length the operator dictates. Equals takes two operands, Not takes one, Between takes three, InList takes at least two. A generated where clause that supplies the wrong number for an operator is structurally invalid regardless of what the operands contain.
- Where clause referencing types the server cannot evaluateOfType with a type the server does not know, or an operator such as RelatedTo that the server does not implement, can invalidate the whole filter. Support for the richer content filter operators is uneven, and a filter that works on a desktop server may be refused by an embedded one.
- Filter sent on an item that is not an event notifierAn EventFilter on a node whose EventNotifier attribute does not permit subscribing to events has nothing to filter. Depending on the server this surfaces as the filter being invalid rather than as an attribute error, so check the node before rewriting the filter.
- Filter built against a different server's namespace indicesA filter serialised from one server and replayed against another carries namespace indices that mean different things. Enough of the select clauses then fail that the whole filter is invalid. This is the standard failure when a filter is stored in configuration rather than built at connect.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Subscribe with the minimal known-good filter firstCreate an event item on the Server object with select clauses for EventId, EventType, SourceNode, SourceName, Time, ReceiveTime, Message and Severity taken from BaseEventType, and no where clause. If that fails, the problem is the item or the node, not the filter content.
- Check the EventNotifier attribute of the nodeRead the EventNotifier attribute on the node you are monitoring. It must indicate that the node subscribes to events. The Server object is the usual entry point for a server-wide event stream; alarm areas and equipment objects expose their own.
- Add the where clause last and validate arityBuild the where clause only after the select clauses work. Assert the operand count against the operator before sending: two for the binary comparisons, one for Not, three for Between, two or more for InList. A unit test over your filter builder catches this class of bug permanently.
- Filter by severity or condition class before filtering by typeA simple where clause comparing Severity against a literal, or comparing ConditionClassId against a known class, is widely supported and covers most real requirements. Reserve the more expressive operators for servers where you have confirmed support.
- Build filters at connect, never load them from stored configurationResolve event type NodeIds and BrowseName namespace indices from the live server each time you connect. Store the model-level intent - type URI and field name - in configuration and turn it into a filter at runtime.
- Read the filter result even on total failureMany servers still return an EventFilterResult alongside the rejection. It usually shows whether the select clauses or the where clause is at fault, which halves the search space immediately.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| UA-.NETStandard (C#) | The EventFilter and ContentFilter builders enforce operator arity as you build, which prevents most structural errors. Inspect the EventFilterResult from the monitored item creation before assuming the filter was accepted. |
| node-opcua | constructEventFilter with an array of field names and an optional where clause produces a valid structure and resolves the fields against BaseEventType. Prefer it to assembling ContentFilterElement arrays. |
| asyncua (Python) | subscribe_events with an event type produces a complete select clause set for that type. Passing a custom where clause is where structural errors appear; validate it before sending. |
| Eclipse Milo (Java) | The content filter builder is fluent and enforces arity, and events arrive as a field array in select clause order. Keep that ordering in your consumer or the fields will look shuffled. |
| open62541 (C) | You build UA_EventFilter with explicit select clause and where clause arrays, so arity checks are on you. Its event subscription support is narrower than its data-change support, so verify what the server and client pair actually accept. |
| UaExpert | The Event View builds a valid filter from the event type you select, so use it to confirm the server delivers events at all before debugging your own filter. If UaExpert sees events and your client does not, the filter is yours to fix. |
What it looks like in the log
CreateMonitoredItems for event item on ns=0;i=2253 returned BadEventFilterInvalid
ContentFilterElement 0: operator Equals with 3 operands
EventFilter contained 0 select clauses
Confirm the fix
Confirm the minimal filter delivers events, then reintroduce your clauses and confirm the filter result is Good throughout and that a deliberately raised condition arrives with every selected field populated.
Stop it happening again
Keep a known-good minimal filter in your codebase as a fallback and a test, generate filters at connect from live type information, and unit test operator arity in your filter builder so a structurally invalid where clause cannot be sent.
Stuck on more than one of these? The troubleshooting clinic works through broken configurations end to end, on camera.
Troubleshooting clinic →Last verified 7 September 2026 against OPC UA 1.05.07. OPC UA Connect is an educational resource and is not affiliated with the OPC Foundation.