OPC UA BadNodeIdUnknown: Resolving Numeric NodeIds

Jason IP3 min read
OPC / OPC UAOther ManufacturerTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

BadNodeIdUnknown occurs here because the client constructs a string NodeId for Temperature, while the server exposes that variable with numeric NodeId NS4|Numeric|23. Keep OpcUa_Attributes_Value; changing the attribute does not correct an invalid NodeId.

Identify the NodeId mismatch

Item Configured or reported value Meaning
Namespace URI http://www.unifiedautomation.com/customprovider/ Stable namespace identity stored in the client configuration
Runtime namespace index 4 Current index resolved from the server's NamespaceArray
BrowseName Temperature Qualified browse name, not the NodeId identifier
Actual NodeId NS4|Numeric|23 Numeric identifier 23 in namespace index 4
Read attribute OpcUa_Attributes_Value Attribute required to read the temperature value

The configuration entry NS4/Variable01 =Temperature passes a string into setNodeId(..., g_nsIndex). That creates a string-identifier NodeId. The server instead expects numeric identifier 23, so the address does not exist and the read returns BadNodeIdUnknown.

Separate NodeId, BrowseName, and DisplayName

A NodeId contains a namespace index and exactly one identifier. The supported identifier types are numeric, string, GUID, and ByteString. A node does not simultaneously use its numeric identifier and BrowseName as interchangeable addresses.

BrowseName identifies a node within a browse path. DisplayName is localized text intended for user interfaces. A browsing client receives the full NodeId in each browse result, displays the returned name, and retains the NodeId for reads and subscriptions. Seeing Temperature in a browse tree therefore does not mean the variable has a string NodeId.

Choose a production addressing strategy

Strategy Required change Use when
Read the numeric NodeId Extend readConfiguration to parse and create numeric identifiers The server must retain NS4|Numeric|23
Expose a string NodeId Modify the C server to assign a string identifier to the variable The existing C++ sample configuration should continue treating variable entries as strings
Resolve a browse path Call TranslateBrowsePathToNodeID with a known starting node and relative BrowseName path Configuration identifies the variable by its location in the address space rather than by a stored identifier

Do not treat namespace index 4 as persistent. Store the namespace URI plus the identifier, read the server's NamespaceArray at startup, find the URI's current index, and replace the stored index before reading. If the numeric identifier itself can change during development, either stabilize the server's NodeId or resolve the node from a known browse path.

Configure and read the variable

  1. Inspect the browse result or attribute view and confirm both the identifier type and identifier. For the reported server, Temperature is NS4|Numeric|23.
  2. Retain NS4/NameSpaceUri = http://www.unifiedautomation.com/customprovider/ and resolve its current namespace index from NamespaceArray when the session starts.
  3. If retaining numeric NodeIds, enhance client_cpp_sdk.cpp so readConfiguration accepts numeric NodeId syntax. UaNodeId::fromXmlString can parse ns=1;i=123 for a numeric identifier or ns=1;s=stringnodeid for a string identifier; then replace the parsed namespace index with the index resolved from the URI.
  4. If retaining the sample's string-only configuration logic, change the server variable to use a string NodeId and configure that exact string identifier.
  5. Build each read request with the resolved NodeId and OpcUa_Attributes_Value.
nodesToRead.create(count);
for (i = 0; i < count; i++)
{
    g_VariableNodeIds[i].copyTo(&nodesToRead[i].NodeId);
    nodesToRead[i].AttributeId = OpcUa_Attributes_Value;
}

Setting Identifier.Numeric = 23 and IdentifierType = OpcUa_IdentifierType_Numeric proved the diagnosis, but hardcoding those fields inside the read loop is only a test. Construct the correct NodeId during configuration processing so reads and subscriptions use the same resolved address.

Verify the correction

  1. Dump or inspect the constructed NodeId before the read. Confirm the namespace index matches the URI's current position and the identifier is numeric 23, unless the server was changed to a string identifier.
  2. Read with OpcUa_Attributes_Value and confirm that BadNodeIdUnknown no longer occurs and the returned value is the server's temperature.
  3. Create a subscription using the same NodeId and confirm that value changes arrive. Successful reads and subscriptions were reported after changing the server variables to string NodeIds.

If browsing succeeds but reading still fails, compare the full browsed NodeId against the constructed read NodeId. Matching display text is insufficient; namespace index, identifier type, and identifier value must all address the same node.

FAQ

Why does OPC UA return BadNodeIdUnknown for Temperature?

The client creates a string NodeId from Temperature, but the server exposes the variable as numeric NodeId NS4|Numeric|23. Configure the numeric identifier or change the server to expose a string NodeId.

Can I read an OPC UA value by changing AttributeId to BrowseName?

No. Use OpcUa_Attributes_Value to read the temperature value. Changing the requested attribute does not repair an incorrect NodeId.

How do I handle an OPC UA namespace index that changes?

Persist the namespace URI and identifier, read NamespaceArray at startup, resolve the URI's current index, and update the NodeId before reading or subscribing.

Back to blog