BadSecureChannelTokenUnknown
BadToken renewal
BadSecureChannelTokenUnknown means the channel security token used on your message has expired or is unknown to the peer. Channel tokens are renewable and short-lived by design; the fix is to renew before expiry and to keep client and server clocks in sync so both agree on lifetime.
What you actually see
A long-running connection fails at a regular interval - often on the hour or on a fixed multiple of minutes - and recovers after a reconnect, then fails again on the same cadence.
Why it happens
- Renewal did not happen in timeEach channel token has a lifetime, and the client is expected to renew well before it lapses. A client blocked by synchronous work, a starved event loop, or a saturated link can miss the window. Once the old token lapses and no new one is in place, every message is rejected.
- Clock skew between client and serverToken lifetime is evaluated against wall-clock time on both ends. A few minutes of drift makes the server consider a token expired that the client still thinks is fresh. The same skew also produces BadCertificateTimeInvalid on perfectly valid certificates, which is a useful confirming signal.
- Both old and new tokens in flight during renewalDuring renewal there is a brief period where messages may arrive under either token. A peer that discards the previous token immediately, rather than accepting it until in-flight messages drain, rejects legitimate traffic.
- A very short configured token lifetimeSome deployments shorten the requested lifetime for security reasons. On a high-latency or intermittent link, a short lifetime turns every hiccup into a lapsed token. The result is a connection that appears to work but drops on a fixed rhythm.
- Requests replayed after a reconnectRetry logic that resends a queued request after the channel has been renewed or replaced signs it with stale key material. The server then reports an unknown token even though the current channel is healthy.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Fix the clocks firstPut NTP or PTP on the client, the server and any gateway between them, and confirm agreement to within a second. On Linux, timedatectl show and chronyc tracking; on Windows, w32tm /query /status. Clock skew is the cheapest cause to eliminate and one of the most common.
- Look at renewal timing in the logFind the interval between OpenSecureChannel renewals and compare it with the token lifetime the server granted. Renewals landing at or past the lifetime, rather than comfortably inside it, identify the problem.
- Unblock the clientIf renewal is late because the client thread or event loop is busy, move data processing off the OPC UA thread. In asyncua and node-opcua, any synchronous work in a callback delays protocol housekeeping including renewal.
- Lengthen the requested token lifetime on a poor linkWhere round-trip times are high or the link is intermittent - cellular, satellite, congested plant wireless - a longer lifetime is the pragmatic choice. Change it in the client's channel configuration and confirm the server's revised value.
- Never retry a request across a channel changeDiscard queued requests when the channel is renewed or replaced and re-issue them as new requests. This removes the stale-signature class of this fault entirely.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| UA-.NETStandard (C#) | The channel renews its token automatically well before expiry. Persistent occurrences here almost always trace to clock skew or a blocked thread pool rather than to the stack. |
| node-opcua | The secure channel layer schedules a renewal based on the granted lifetime. Heavy synchronous processing in a monitored-item callback is the usual reason the renewal timer fires late. |
| asyncua (Python) | Renewal runs as an asyncio task. Any blocking call - a synchronous database write, a slow file operation - inside a coroutine on the same loop starves it and produces this error on a regular cadence. |
| Docker | Containers inherit the host clock, but a suspended or migrated VM host can jump time. If the fault correlates with host maintenance, chase time sync rather than OPC UA configuration. |
| Wireshark OPC UA dissector | TokenId is visible on each UASC message, so you can see the exact message where the client switched tokens and whether the server had already stopped accepting the previous one. |
What it looks like in the log
Service call failed: BadSecureChannelTokenUnknown - the token has expired or is not recognized
Renewing security token for channel 7 (lifetime 3600000 ms granted)
Rejected message on channel 7: tokenId 2 unknown, current tokenId is 3
Confirm the fix
Run the connection across several token lifetimes with no faults, and confirm from the log that each renewal completes at a comfortable margin inside the granted lifetime.
Stop it happening again
Treat time synchronisation as part of the OPC UA deployment standard, keep protocol threads free of application work, and pick a token lifetime appropriate to the worst link in the path rather than the best.
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.