Python · recipe

Handle errors and reconnect in Python

Wrap the session in a loop that reconnects with backoff, and let asyncua try to transfer your subscriptions rather than rebuilding them. Branch on the status code you actually got: the specific code almost always names the real problem.

Stackasyncua (FreeOpcUa/opcua-asyncio)
Version2.0.1

The code

import asyncio
from asyncua import Client, ua

URL = "opc.tcp://opcua.umati.app:4840"

class Handler:
    def datachange_notification(self, node, val, data):
        print(node, val)

    def status_change_notification(self, status):
        print("subscription status changed:", status)

async def run_once():
    async with Client(url=URL) as client:
        client.session_timeout = 30000
        sub = await client.create_subscription(1000, Handler())
        await sub.subscribe_data_change(client.get_node("i=2258"))
        while True:
            await asyncio.sleep(1)
            await client.check_connection()   # raises when the session is gone

async def main():
    delay = 1
    while True:
        try:
            await run_once()
        except (ua.UaStatusCodeError, ConnectionError, asyncio.TimeoutError) as e:
            print(f"disconnected ({e}); retrying in {delay}s")
            await asyncio.sleep(delay)
            delay = min(delay * 2, 60)          # backoff, and stagger across a fleet
        else:
            delay = 1

asyncio.run(main())

What it prints

i=2258 2026-09-08 03:45:00.102000
disconnected (BadSessionIdInvalid); retrying in 1s
i=2258 2026-09-08 03:45:14.331000
subscription status changed: StatusCode(Good)

Notes

  • Exponential backoff with a cap, and jitter if you run many clients. Two hundred collectors reconnecting in lockstep after a network blip will take the server down more reliably than the blip did.
  • `check_connection()` is how you notice a dead session promptly rather than waiting for a read to fail.
  • On reconnect, prefer transferring subscriptions over recreating them. GoodSubscriptionTransferred is the success signal.
  • Do not retry a certificate error. BadCertificateUntrusted will fail identically forever until someone changes a trust list: log it loudly and stop.
  • Distinguish the three families: certificate and identity errors are configuration, BadTooMany* are capacity, and BadSessionIdInvalid or BadConnectionClosed are transient.

What breaks first

A client retries a permanent configuration error forever and floods the logs BadCertificateUntrusted
Classify status codes before retrying. Certificate, identity and not-supported errors need a human; only transient transport and session errors deserve a retry.

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.

Free download
OPC UA Troubleshooting & Integration Checklist
The field-tested checklist engineers use to diagnose certificates, ports and namespace problems fast, plus a Python (asyncua) client template.
Get the free checklist →
Free, no course, no login