OPC UA Namespace Index Changes: Resolve Stable Node Identities After Restart
Persist a node reference using its namespace URI, identifier type and identifier value, then resolve the URI through the target server’s current NamespaceArray. Do not persist only a numeric namespace index and assume it still identifies the same naming authority after a new Session. A stale index can resolve to an existing but entirely different node, making this more dangerous than a simple “node unknown” error.
This guide pins OPC UA Part 3, version 1.05.06, sections 5.2.2 and 8.2, and Part 5, version 1.05.06, section 6.3.1. An original namespace-resolution fixture was executed offline. No server restart, SDK resolver or live node access was tested.
Distinguish identity from its compact encoding
A NodeId contains NamespaceIndex, IdType and Identifier. The namespace URI identifies the naming authority; the index is its position in the server’s namespace table. Part 3 requires the server to persist the identifier type, identifier and namespace URI for a node, while allowing its numeric namespace index to change with future Sessions.
Keep the identifier type intact. Numeric identifier 42 and string identifier “42” are different references. Likewise, string identifiers and namespace URIs are case-sensitive. Do not normalize case, remove punctuation or convert every identifier into an untyped display string.
Bind the persistent reference to the intended server/application context as well. A familiar namespace URI does not authenticate an endpoint, establish trust or prove that firmware preserved every node’s meaning. Node identity and server security are separate contracts.

An original restart fixture
The hypothetical server initially exposes the standard OPC UA namespace at index zero, its local namespace at one and urn:example:line at two. After restart, a vendor namespace occupies two and the line namespace moves to three.
| Index | Initial table | New-session table |
|---|---|---|
| 0 | Standard OPC UA namespace | Standard OPC UA namespace |
| 1 | urn:example:server | urn:example:server |
| 2 | urn:example:line | urn:example:vendor |
| 3 | Not present | urn:example:line |
The persistent reference is the tuple urn:example:line, String, Motor07.Speed. Resolving it against the new table gives index three. Reusing index two instead selects the vendor namespace. A successful read from that stale reference would not prove correct binding if a similarly named identifier exists there.
The local fixture checked both results and preserved the distinction between numeric 42 and string “42.” Its strings and server identity are artificial. The test establishes cache-selection behavior, not any real server’s restart policy.
Scope resolved references to a validated generation
- Authenticate and select the intended server through the application’s existing security policy.
- At Session establishment, obtain the server’s current NamespaceArray and validate the read result.
- Find the exact namespace URI for every required persistent reference; fail visibly if it is unavailable.
- Construct the runtime NodeId with the resolved index and unchanged typed identifier.
- Check the expected NodeClass, datatype and relevant model contract before enabling consequential operations.
- Publish the new resolution cache atomically, tagged with the server and local Session/cache generation.
Do not let readers combine an old index with a new identifier map during rebuild. An asynchronous lookup or queued operation should carry the generation it was resolved against. Reject or re-resolve work from a retired generation before sending it through a new Session.
For missing namespaces, expose a configuration or compatibility failure. Falling back to zero, one or the previous index can target an unrelated namespace. If the table contains an unexpected ambiguous match, a conservative resolver should hold the binding for investigation rather than selecting the first entry silently.
Understand what may change while connected
Part 5 prohibits modifying or deleting existing NamespaceArray entries while any client has an open Session, because clients may cache the table. A server may append entries while clients are connected. It recommends preserving indexes where possible, but clients caching indexes should check them when starting a Session.
Therefore a new namespace appended during a Session does not imply that all existing indexes changed. The fixture verified that appending a namespace preserved the already resolved line index. Refresh the table when a newly needed namespace is absent, while retaining the generation and error-handling rules.
Keep the scope precise: the index belongs to the server’s namespace table, not to an arbitrary process-global registry shared across every endpoint. Two servers can assign different indexes to the same URI. One cache keyed only by URI and reused across connections can still produce incorrect runtime NodeIds.
Do not substitute display names for identifiers
A DisplayName is intended for presentation and may be localized. A BrowseName can be useful in a documented browse-path contract, but it is not globally unique within the server. Searching for the first node whose displayed text resembles the old reference is not an identity repair.
If firmware intentionally replaces or removes nodes, treat that as a model migration. Resolve the documented new identity or supported browse path, validate its type and meaning, and update the application contract through the approved process. Namespace-table refresh alone cannot prove semantic compatibility after an arbitrary model redesign.
Qualification evidence
The executed fixture covered index movement, accidental old-index binding to another namespace, append-only growth, typed identifiers, generation mismatch and four invalid cases: changed URI case, missing URI, wrong identifier type and an ambiguous fixture table. Its resolver supported only numeric and string identifiers; GUID and opaque identifiers require their own typed handling.
For the selected server, run a controlled restart or model-configuration change in a lab. Capture both namespace tables, persistent keys, resolved NodeIds and expected node metadata. Add a deliberate neighboring namespace with the same string identifier to detect silent wrong-node success.
Acceptance means the client either finds the intended typed identity in the new table or reports a bounded compatibility failure. Merely reconnecting and obtaining a value is insufficient; the evidence must show why that value still belongs to the intended node.