BadMonitoredItemIdInvalid
BadLifecycle
BadMonitoredItemIdInvalid means the monitored item handle you passed does not exist in that subscription. It comes from stale handles after a rebuild, from handles used against the wrong subscription, or from an item whose creation actually failed. Re-read the ids returned by CreateMonitoredItems and rebuild your local map.
What you actually see
ModifyMonitoredItems, SetMonitoringMode, SetTriggering or DeleteMonitoredItems returns this code for some items while other items in the same call succeed.
Why it happens
- Item ids cached across a subscription rebuildMonitored item ids are scoped to a subscription and are reissued when the subscription is recreated. Client code that stores ids in a configuration table or a long-lived dictionary, then reconnects, sends the old numbers into a new subscription. Every id is then invalid even though the nodes are fine.
- Creation failed but the client recorded the id anywayCreateMonitoredItems returns a per-item result. If an item failed with BadNodeIdUnknown or a filter error, the returned monitored item id is not usable. Client code that reads the results array positionally without checking each StatusCode records a phantom item and then fails on the first modify.
- Handle used against the wrong subscriptionWith several subscriptions on one session - which is normal once you have multiple publishing intervals - it is easy to send an item from subscription A in a request naming subscription B. The ids are small integers and often collide numerically, so the mistake produces this code rather than an obvious mismatch.
- Item already deletedA delete that succeeded, or a server-side removal when the underlying node disappeared after a model change, leaves the client with a handle to nothing. Reconfiguration code that deletes then modifies in the same pass hits this routinely.
- Client-side handle confused with server-side idEach monitored item has a client handle you choose and a server-assigned monitored item id. Passing the client handle where the service expects the server id is a common wiring bug in hand-rolled clients and produces this code for every item.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Check every per-item result from CreateMonitoredItemsWalk the results array and pair each StatusCode with the request you sent. Only store the monitored item id where the status is Good. Log the failures with the NodeId so you can see which items never existed rather than discovering it later on a modify.
- Rebuild the local handle map whenever the subscription is rebuiltMake the id map a child of the subscription object so it cannot outlive it. On reconnect or recreate, discard the map and repopulate it from the fresh CreateMonitoredItems results. Never write these ids to persistent configuration.
- Separate client handle from server id in your data structuresKeep both, name them distinctly, and use the server id only in service calls. Route incoming notifications by client handle, which is what the notification carries, and use the server id for ModifyMonitoredItems, SetMonitoringMode, SetTriggering and delete.
- Verify you are naming the right subscriptionLog subscription id and item id together on every modify. Where a client holds many subscriptions because it has many publishing intervals, an index off by one in your subscription list is a more likely explanation than anything server-side.
- Recreate rather than modify when in doubtDeleting and recreating an item is cheap compared with debugging a stale handle. Where a modify returns this code for a node you know exists, drop the local entry and create the item again, then confirm the new id arrives Good.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| UA-.NETStandard (C#) | MonitoredItem.Status.Id holds the server id and is only valid after ApplyChanges. Check MonitoredItem.Status.Error for each item after ApplyChanges rather than assuming the whole batch succeeded. |
| asyncua (Python) | subscribe_data_change returns a handle per node, and the results list contains failures as exceptions or bad status depending on how you call it. Use the batch form and inspect each entry before recording handles. |
| node-opcua | The monitoredItem object exposes monitoredItemId once the server has confirmed it, and emits err for items that failed. A handler that only listens for changed will never learn that an item was rejected. |
| open62541 (C) | UA_Client_MonitoredItems_createDataChanges returns a response with a per-item monitoredItemId and status. Store the id only for entries whose status is good; the delete and modify calls take that id. |
| Eclipse Milo (Java) | UaMonitoredItem exposes both the client handle and the server-assigned id, and each item carries its own StatusCode from creation. Filter the returned list on isStatusGood before building your map. |
| UaExpert | Rows that failed creation display a bad status in the Data Access View instead of a value. Removing and re-adding the row is the quickest way to get a valid item where the node itself is good. |
What it looks like in the log
SetMonitoringMode: item 217 in subscription 3 returned BadMonitoredItemIdInvalid
CreateMonitoredItems: 4 of 512 items failed; ids not recorded
ModifyMonitoredItems failed for cached handle 91 after subscription rebuild
Confirm the fix
Re-issue the same modify or delete after rebuilding the map and confirm every per-item status is Good, then confirm the affected nodes resume reporting notifications at the expected interval.
Stop it happening again
Scope monitored item ids strictly to the lifetime of their subscription object, never persist them, and make per-item result checking mandatory in the code path that records handles so a failed creation can never enter the map.
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.