BadNodeIdUnknown
BadNamespace index
BadNodeIdUnknown means the NodeId was well formed but the server has no such node. The classic cause is a shifted namespace index after a PLC program download or a server upgrade. Read NamespaceArray at connect, map URI to index, and store the namespace URI plus the identifier in configuration - never the raw index.
What you actually see
A client that has worked for months suddenly fails on some or all of its nodes right after a controller download, a firmware update or a server reconfiguration, while browsing from the root still works fine.
Why it happens
- The namespace index shiftedNamespaceArray maps namespace index to namespace URI, and index 0 is always the OPC UA namespace. Every other index is assigned per server and is not stable across restarts or reconfiguration. Add a companion specification, download a changed PLC program, or upgrade firmware, and what was ns=3 becomes ns=4. This is the single most common source of silently broken integrations.
- The index was hardcoded in configurationA tag list, a published-nodes file or a SCADA tag import holding literal strings like ns=3;s=Machine1.Speed carries no information about which namespace index 3 meant when it was written. It keeps addressing whatever now occupies that slot, or nothing at all.
- The node was removed or renamed by a model changeDeleting a program block, renaming a symbol, or removing an optional object from the model deletes nodes. String identifiers usually derive from the controller symbol name, so a rename is a delete plus an add from the client's point of view.
- You are talking to a different server than you thinkA redundant partner, a standby, a staging device on the same address, or a second instance behind a load balancer can present a different namespace ordering or a different address space entirely. The NodeId is fine; the server is not the one it was written for.
- The identifier is subtly wrongString identifiers are case sensitive and whitespace matters. A trailing space from a spreadsheet import, a changed separator between a block and a variable name, or a GUID formatted differently produces a NodeId the server simply does not have.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Read NamespaceArray and map URI to index at connectRead the NamespaceArray property on the Server object at every connect and rebuild your index map before using any cached NodeId. This is the fix, and everything else on this page is a workaround for not doing it.
- Store the namespace URI and the identifier, never the indexConfiguration should carry the URI plus the identifier - for example http://plc01/PLC1/ together with s=Machine1.Speed - and resolve the index at runtime. opcua-commander accepts exactly this form, nsu=...;s=..., and queries NamespaceArray to resolve it; OPC Publisher likewise lets you name the namespace by full URI instead of by index.
- Prove the shift before changing anything elseCompare the NamespaceArray you read now against the one recorded at commissioning. A URI that moved from position 3 to position 4 explains every failing node at once, and no per-tag investigation is needed.
- Watch for model changes and invalidate cachesSubscribe to GeneralModelChangeEvent on the Server object. When the server reports that its address space changed, drop cached NodeIds and re-resolve rather than waiting for reads to fail. This is what turns a silent outage into a handled event.
- Re-resolve by browse path where the model is knownUse TranslateBrowsePathsToNodeIds from a stable anchor - the Objects folder, i=85, or a machine object - to recover the current NodeId for a known BrowseName path. It is far more robust than a stored NodeId and far cheaper than a recursive browse.
- Re-resolve after every reconnect, not just at first startA reconnect can follow a restart you did not see. Treat namespace resolution as part of the connect sequence, immediately after ActivateSession and before any Read, Write or subscription creation.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| opcua-commander | Accepts nsu=http://samples.org/UA/MyNamespace;s=MyVariable and automatically queries the server's NamespaceArray at runtime to map the URI to its integer index. This is the canonical demonstration of the correct pattern in a tool you can run in a terminal. |
| Siemens S7-1500 | A program download can change the server namespace content and its ordering. Treat every download as a namespace-invalidating event: re-read NamespaceArray on reconnect and never store ns= indices in a SCADA tag import. |
| OPC Publisher | Published-nodes entries can specify namespaces by index or by full namespace URI. Use the URI form; index-based entries silently point at the wrong data after a controller change rather than failing loudly. |
| asyncua (Python) | client.get_namespace_index(uri) resolves the index from the server at runtime, and client.get_namespace_array() returns the whole list. Build NodeIds from the resolved index rather than writing literals. |
| node-opcua | The session exposes the namespace array and a resolver for namespace URIs. Resolve once after connect, cache the mapping for the life of the session only, and rebuild it on every reconnect. |
| UaExpert | Browsing to a node and reading its NodeId attribute shows the current index. Comparing that against the index in your configuration takes seconds and settles the diagnosis. |
What it looks like in the log
Read failed: BadNodeIdUnknown for ns=3;s=Machine1.Speed
NamespaceArray changed: http://plc01/PLC1/ moved from index 3 to index 4
GeneralModelChangeEvent received; invalidating 812 cached NodeIds
Confirm the fix
Reconnect, read NamespaceArray, and confirm the URI you rely on resolves to the index your client is now using, then read one node from each namespace successfully.
Stop it happening again
Make namespace URI plus identifier the only permitted form in configuration, resolve indices at connect in a shared client helper, and subscribe to GeneralModelChangeEvent so address-space changes invalidate caches instead of breaking reads.
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.