BadFilterOperandInvalid
BadEvent filters
BadFilterOperandInvalid means one operand inside your filter is malformed. In an EventFilter that is nearly always a SimpleAttributeOperand whose TypeDefinitionId or BrowsePath does not resolve, or an ElementOperand whose index points at a nonexistent element. The result tells you which operand, so read the per-operand results before rewriting the filter.
What you actually see
An event subscription is rejected and the response carries an EventFilterResult with a bad status against one specific select or where operand rather than against the filter as a whole.
Why it happens
- SimpleAttributeOperand with a BrowsePath that does not exist on the event typeA select clause operand names a TypeDefinitionId plus a relative BrowsePath, such as the ConditionName property of a ConditionType. If the path is misspelled, uses the wrong namespace for the BrowseName, or names a field the event type does not have, the operand cannot resolve. Namespace index in a QualifiedName is the most common detail to get wrong here.
- TypeDefinitionId that is not an event typeThe TypeDefinitionId in a SimpleAttributeOperand must be an ObjectType in the event type hierarchy, most commonly BaseEventType or a subtype. Passing a Variable's type, an instance NodeId, or a stale numeric id from a different server produces an unresolvable operand.
- ElementOperand index out of range or circularWhere clauses build a tree by having elements reference other elements by index. An index beyond the end of the element array, or a chain that refers back to itself, is invalid. This is common in generated where clauses assembled by appending elements without recomputing indices.
- LiteralOperand with a value of the wrong type for the comparisonComparing a UInt16 Severity against a string literal, or an enumeration against a raw integer where the server expects the enumeration DataType, gives an operand the server cannot use. The Variant's type matters, not just its value.
- AttributeOperand used where only SimpleAttributeOperand is supportedThe full AttributeOperand form with a relative path and index range is more expressive and less widely supported than SimpleAttributeOperand. A server that implements only the simple form will reject the richer operand, and the rejection lands on the operand rather than on the filter.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Read the per-operand results, not just the overall statusThe response carries an EventFilterResult with a status per select clause and per where-clause element and operand. That tells you exactly which operand failed. Log the whole structure; guessing which of twelve select clauses is wrong wastes far more time than printing it.
- Start from a minimal filter and add operands one at a timeBegin with a select clause containing only EventId, EventType, SourceName, Time, Message and Severity from BaseEventType, and no where clause. That filter works on essentially every event-capable server. Add each additional operand and re-create the item, so the first failure names the culprit.
- Resolve BrowseNames with the right namespace indexThe QualifiedName elements in a BrowsePath carry a namespace index that must be resolved from the server's NamespaceArray at connect time, never hardcoded. A field defined by a companion model lives in that model's namespace, not in namespace zero, and an index that shifted after a server upgrade breaks every operand that used it.
- Verify the field exists by browsing the event typeBrowse the ObjectType you named and walk its HasProperty and HasComponent references to see the actual instance declarations. If the field is not there, the operand can never resolve, whatever the filter syntax looks like.
- Recompute element indices whenever you build a where clauseBuild the where clause as a tree in your own code and serialise it to the flat element array in one pass, so indices are always consistent. Appending elements to an existing array by hand is how circular and out-of-range references appear.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| UA-.NETStandard (C#) | Use the EventFilter helper to add select clauses by type and BrowsePath, and inspect the returned EventFilterResult after ApplyChanges. The per-clause SelectClauseResults array names the failing operand directly. |
| node-opcua | constructEventFilter accepts field names and resolves them against the event type, which avoids most operand mistakes. Hand-built SimpleAttributeOperand arrays are where this code shows up. |
| asyncua (Python) | subscribe_events builds the select clauses from the event type you pass, so use it rather than assembling operands. Where you need a where clause, build the element list programmatically and print it before sending. |
| open62541 (C) | You construct UA_SimpleAttributeOperand arrays directly, including the QualifiedName browse path and the attributeId, which must be the Value attribute for a field. The filter result is returned in the monitored item response. |
| Eclipse Milo (Java) | The EventFilter builder resolves fields against a type and returns the filter result per clause. Check the result rather than only the item status, since a partially valid filter can still be reported per operand. |
| UaExpert | The Event View lets you pick fields from the browsed event type, which is a reliable way to discover the exact BrowsePath and namespace index a field really has before coding it. |
What it looks like in the log
EventFilterResult: selectClauseResults[3] = BadFilterOperandInvalid
SimpleAttributeOperand browsePath 2:ConditionName unresolved on type ns=0;i=2782
Where clause element 4 references element index 9; array length 6
Confirm the fix
Recreate the item and confirm every entry in the filter result is Good, then trigger a real event on the server and confirm each selected field arrives populated rather than null - a null field usually means the operand resolved to nothing.
Stop it happening again
Generate select clauses from a browse of the event type rather than from a static list, resolve namespace indices from the NamespaceArray at every connect, and build where clauses as a tree that is serialised in one pass so indices cannot drift.
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.