BadTcpSecureChannelUnknown
BadProtocol
BadTcpSecureChannelUnknown means the transport layer received a message whose SecureChannelId and TokenId it does not hold. It is the transport-level counterpart of BadSecureChannelIdInvalid and usually means the server restarted, the channel was closed, or a load balancer sent you to a different instance.
What you actually see
Messages are rejected at the framing layer immediately after what looked like a successful reconnect, or immediately after a server restart while the client still had a socket open.
Why it happens
- Server restarted and the channel table is goneChannel and token identifiers are in-memory server state. After a restart the client's cached ids match nothing. If the client re-established only the socket and reused the old channel id, every message is refused here.
- Message sent on a channel that was already closedQueued requests flushed after CloseSecureChannel, or a retry issued from a background thread after an error path closed the channel, arrive with an id the server has released.
- Token no longer currentThe pair of channel id and token id must both be recognised. A message signed with a token that has been superseded and dropped is refused, which is closely related to BadSecureChannelTokenUnknown.
- Requests landing on the wrong server instanceTwo servers behind one address, whether by load balancer, DNS round-robin or a Kubernetes Service with several replicas, each keep their own channel table. The client is correct and the infrastructure is wrong.
- Framing confusion from a middleboxA proxy that buffers or splits the byte stream, or a TLS-terminating device in front of opc.tcp, can present the server with bytes it parses as a channel id that was never issued.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Check for a server restartRead Server_ServerStatus_StartTime after reconnecting, and compare with when your channel was opened. A restart makes this expected; the follow-up question is whether the client discards cached channel state properly.
- Ensure a channel error clears all channel stateOn any channel-level error, drop the channel id, token id and derived keys, close the socket, and open a fresh channel. Never retry the failed request on the old identifiers.
- Prove you are reaching one server instanceConnect several times and compare ApplicationUri and the server certificate thumbprint. Differences from a single URL mean traffic is being spread across instances.
- Remove round-robin and TLS-terminating middleboxesopc.tcp needs a single stable peer. Use a StatefulSet with per-pod addressing in Kubernetes, or a plain DNAT with no reassembly, and never a layer-7 proxy in front of opc.tcp.
- Capture the framing if it persistsWith the Wireshark OPC UA dissector, read SecureChannelId and TokenId on each UASC message and compare with what the server issued in the OpenSecureChannel response.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| UA-.NETStandard (C#) | The transport channel owns these identifiers. Recreate the channel object rather than trying to repair it; the SessionReconnectHandler does this for you. |
| open62541 (C) | The UA_SecureChannel state is tracked separately from the session, so the client state callback tells you clearly that the failure is at channel level. |
| node-opcua | ClientSecureChannelLayer will rebuild the channel on reconnection. This code appearing repeatedly points at duplicate channels or a load-balanced endpoint rather than at the stack. |
| Kubernetes | A Service with multiple replicas is the classic cause. Give each server pod its own stable endpoint and address it directly. |
| Wireshark OPC UA dissector | Shows the OPN response carrying the issued channel and token ids, then every MSG that references them - which makes a wrong-instance problem obvious within a few frames. |
What it looks like in the log
BadTcpSecureChannelUnknown - the SecureChannelId and TokenId are not currently in use
Discarding message for unknown channel 33/token 2 from 10.20.3.15
OpenSecureChannel issued channelId=34 tokenId=1 after reconnect
Confirm the fix
Reconnect and confirm a new OpenSecureChannel with fresh identifiers, then sustain traffic with no transport-level rejections across at least one token renewal.
Stop it happening again
Treat secure channel state as disposable in client code, pin OPC UA endpoints to a single server instance, and keep layer-7 proxies and round-robin load balancers off opc.tcp entirely.
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.