BadSessionClosed
BadLifecycle
BadSessionClosed means the session you are addressing existed but has been closed - by your own CloseSession, by the server on timeout, or by an administrator. Unlike a transient network fault, retrying the same session never works: create and activate a new session, then transfer or rebuild subscriptions.
What you actually see
Calls fail immediately with BadSessionClosed rather than timing out, often right after a shutdown path or a reconnect ran in parallel with normal work.
Why it happens
- The client closed the session itselfA shutdown handler, a using or with block, or a disposed client object called CloseSession while other threads or tasks still hold the session. The server has already torn down the per-session diagnostics and subscriptions, so every later request is refused.
- Server closed it on timeoutThe server aged the session out after the revised session timeout with no traffic, and reports the closed state rather than an unknown id because it still has the record. Which of BadSessionClosed or BadSessionIdInvalid you see depends on how long the server retains closed session bookkeeping.
- Administrative disconnectMany servers expose a disconnect action for an operator, and some enforce policy limits such as a maximum session lifetime or a user lockout. Siemens and Beckhoff runtimes drop sessions on a program download or a configuration activation.
- DeleteSubscriptions on close raced with in-flight PublishCloseSession takes a deleteSubscriptions flag. Closing a session with outstanding Publish requests makes those Publishes complete with a fault; code that treats the Publish response as authoritative then reports BadSessionClosed from a background thread long after the intentional shutdown.
- Two clients sharing one session objectIn gateway and collector deployments it is common to share one session across many worker tasks. When one worker's error path closes the session, every other worker sees BadSessionClosed with no local explanation.
How to fix it
In diagnostic order: work down, do not skip ahead.
- Establish who closed itCheck your own client log for a CloseSession call first. If the client never issued one, look at the server's audit trail or log - servers that emit audit events record session close with the reason and the originating user.
- Separate intentional shutdown from failureSet a shutting-down flag before calling CloseSession and suppress reconnect logic while it is set. This removes the most common false alarm, where an orderly shutdown produces a burst of BadSessionClosed faults in monitoring.
- Own the session lifecycle in one placeGive a single component responsibility for creating, activating and closing the session, and hand workers a handle they cannot close. This is the fix for shared-session gateway code.
- Recreate the session and transfer subscriptionsCreate a fresh session on the existing secure channel where the stack supports it, activate it, then call TransferSubscriptions for the previous subscription ids and treat GoodSubscriptionTransferred as success.
- Check for a server-side maximum session lifetimeIf sessions close on a regular cadence regardless of activity, the server is enforcing a lifetime or a re-authentication policy rather than an idle timeout. Confirm with the vendor configuration and schedule your own reconnects ahead of it.
Where it shows up, by tool and stack
| Tool / stack | What to do there |
|---|---|
| UA-.NETStandard (C#) | Disposing the Session object calls CloseSession. In long-running services, keep the Session in a container-managed singleton; disposing it inside a scoped request handler is a classic source of this fault. |
| node-opcua | session.close() and client.disconnect() both end it. Pending readValue or subscription callbacks then reject with BadSessionClosed; guard your callbacks with a closed flag. |
| asyncua (Python) | Exiting the async with Client(...) block closes the session. Background tasks that outlive the block will raise BadSessionClosed - cancel them before leaving the context manager. |
| Beckhoff TwinCAT | Activating a configuration or restarting the runtime closes all OPC UA sessions. Expect this on every deployment and treat it as planned, not as a fault. |
| Siemens S7-1500 | A full program download stops the OPC UA server and closes sessions; the address space may also come back with different namespace indices, so re-resolve NamespaceArray after reconnect. |
What it looks like in the log
Publish response returned BadSessionClosed; abandoning subscription 4
CloseSession requested by client (deleteSubscriptions=true), session closed
Service call rejected: session has been closed by the server
Confirm the fix
Reconnect and confirm the new session survives an idle period longer than the revised timeout. Then run your shutdown path and confirm it produces no reconnect attempts and no error-level log lines.
Stop it happening again
Make session ownership explicit in code and mark planned disconnects as planned. On PLC and controller servers, treat a download or configuration activation as a scheduled session close and re-resolve namespaces on reconnect.
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.