A client writes "b" to the String variable, emits two warning messages, and then receives BadInternalError (0x80020000) on readback. Two separate faults are involved: the server setter converts the String to a number, while one client-node version emits warnings even when the write succeeds.
How does the write travel from client to variable?
Follow the packet from the Node-RED inject node to the OPC UA address space. The inject node supplies msg.payload; the OpcUa-Item node adds the target NodeId and datatype; the OpcUa-Client issues the Write service request; and the compact server invokes the variable's setter. A later Read request invokes the getter and returns a Variant to the client.
| Path element | Configured value | Diagnostic significance |
|---|---|---|
| Write payload | "b" |
A valid JavaScript string entering the write path |
| Item NodeId | ns=1;s=count |
Must match the server variable exactly |
| Item datatype | String |
Matches the address-space datatype |
| Endpoint | opc.tcp://localhost:4840 |
Uses the local host loopback path |
| Security policy and mode | None |
No certificate or secure-channel policy mismatch is involved |
| Server access | Anonymous allowed | No username authentication blocks the request |
| Read-client time setting | Controls the configured read activity timing | |
| Write-client time setting | Recorded write-node timing setting; it does not repair a datatype mismatch | |
| Server initialization delay | Requests must occur after address-space construction |
Layer one is short in this installation because both endpoints use localhost. There is no external cable, switch, or routed hop between the client and server. The successful initial read and successful numeric-variable operations also show that the endpoint, TCP connection, OPC UA session, NodeId routing, and basic service path work. The failure begins after the String setter receives the Variant.
Which symptoms identify each failure point?
| Symptom | Failure point | Deciding test |
|---|---|---|
| Initialized String reads correctly | Getter and initial backing value are valid | Read before making a write |
| Integer and Double variables read and write correctly | Transport and general client/server operation are functional | Compare service results through the same endpoint |
Read fails after writing "b"
|
String backing value was corrupted by the setter | Inspect the value assigned inside set
|
BadInternalError (0x80020000) appears during active reading |
The getter cannot return a valid String Variant from the stored value | Remove numeric conversion and repeat write/readback |
Counter: Second set value by Input b |
Client-node input processing warning | Check the Write result and server readback separately |
| Payload warning shows a serialized message structure | Client-node diagnostic behavior rather than proof of an OPC UA write failure | Read the target directly after the write |
Warnings appear in 0.2.284 but not 0.2.248
|
Version-dependent warning behavior | Run the same nodes and payload under both versions |
The read error and client warnings must not be treated as one event. Removing parseFloat() restores readable server data. Separately, the warnings can remain while correct values are written. A Node-RED warning is not an OPC UA StatusCode; judge the transaction by its service result and readback value.
Which correction approach fits this case?
| Approach | What it changes | When to use it | Limitation |
|---|---|---|---|
| Correct the server setter | Stores the incoming String without numeric conversion | Required whenever the variable is declared String
|
Does not necessarily remove version-specific client warnings |
| Align the client item definition | Targets ns=1;s=count with datatype String
|
Required for every write path to this variable | The shown flow is already aligned |
| Verify and tolerate the warning | Leaves the installed client-node version unchanged | Use only when the Write result is good and independent readback equals the sent string | Warnings may obscure a later real fault |
Use version 0.2.248
|
Removes the reported warnings for the same nodes | Use when warning-free operation is operationally necessary and version change is acceptable | It does not correct a server setter that converts Strings to numbers |
Correct the setter first. Then verify the write under the installed version. If readback is correct but 0.2.284 continues to emit the two messages, classify them as version-specific diagnostics. Moving to 0.2.248 is a warning-suppression workaround, not the datatype fix.
Why does parseFloat() break the String variable?
The variable declares dataType: "String", and its getter constructs a Variant with DataType.String. That establishes a contract: the backing variable count must remain a string.
The original setter violates that contract:
set: function(variant) {
count = parseFloat(variant.value);
return opcua.StatusCodes.Good;
}
For input "b", JavaScript parseFloat() produces NaN. The setter nevertheless returns Good, so the client can see an apparently successful write even though the backing value is no longer valid for a String Variant. On the next read, the getter labels that non-string value as DataType.String, and the server surfaces BadInternalError (0x80020000).
A numeric-looking string also remains the wrong design. Converting "2" produces the number 2, not the string "2". The getter's declared datatype and the stored JavaScript value still disagree. A server must not acknowledge a write as Good after silently changing the value into an incompatible type.
How should the address-space variable be corrected?
Keep the getter and setter symmetrical: accept a String Variant, store its value as a string, and return it as a String Variant. The direct correction is:
let count = "a";
namespace.addVariable({
"componentof": Tanks,
"nodeId": "ns=1;s=count",
"browseName": "Counter",
"dataType": "String",
"value": {
"get": function() {
return new Variant({
"dataType": DataType.String,
"value": count
});
},
"set": function(variant) {
count = variant.value;
return opcua.StatusCodes.Good;
}
}
});
- Remove
parseFloat()from the setter. - Retain
let count = "a";so the initial backing value is a string. - Retain
dataType: "String"in the variable declaration. - Retain
DataType.Stringin the returned Variant. - Deploy or restart the flow so the address space is reconstructed with the corrected setter.
- Wait until address-space construction completes before issuing the first test read; the compact server is configured with a initialization delay.
If the application actually requires numeric storage, declare and handle a numeric OPC UA datatype throughout instead of presenting the node as a String. Conversion belongs at a defined application boundary, not inside a setter whose public datatype is String.
How should the client write path be configured?
The supported write path uses an inject node, an OpcUa-Item, and an OpcUa-Client configured for write. The item node carries the address and datatype metadata; the inject node carries the new value.
- Set the inject payload type to string and enter
bas the payload. - Set the item to
ns=1;s=count. - Set the item datatype to
String. - Connect the item to the client whose action is
write. - Use endpoint
opc.tcp://localhost:4840with the configuredNonesecurity policy and mode. - Attach a full-message debug node to the write client so the service result can be separated from warning text.
- Use a separate read client and item for the same NodeId, then display its payload in a debug node.
The message that includes an array of message fields and values is generated while the client node interprets its input. It does not by itself establish that the server stored that array. Inspect the OPC UA write status, then read ns=1;s=count through the server. If the returned value is "b", the data path completed despite the warning.
What proves that the repair is complete?
- Restart the corrected server and read
ns=1;s=countbefore writing. Confirm that the initialized value is"a". - Inject the string
"b"through the write path. - Check that the write operation does not return a bad OPC UA service result.
- Read the same NodeId immediately and confirm that the payload is exactly
"b", not a number,NaN, or a serialized Node-RED message. - Allow the configured read activity to repeat and confirm that
BadInternalError (0x80020000)does not recur. - Write a numeric-looking string such as
"2"and confirm readback remains a String value rather than being converted to a number. - If warnings remain under
0.2.284, repeat the identical write and readback under0.2.248. Treat the version change as validated only when the warning disappears without changing the stored value.
FAQ
What happens if I use parseFloat() in an OPC UA String setter?
"b" becomes NaN, while "2" becomes the number 2. Both violate the backing-value contract for a variable returned as DataType.String, and the next read can fail with BadInternalError (0x80020000).
What happens if version 0.2.284 warns but the value is written?
Judge the operation by the OPC UA service result and an independent read of ns=1;s=count. The same nodes were reported to run without those warnings under 0.2.248, so changing version is an option when warning-free logs are required.
How do I verify an OPC UA String write?
Write "b" with the item datatype set to String, then read the same NodeId through a separate read path. The final verification step is confirming repeated readback equals "b" and no longer returns BadInternalError (0x80020000).