C · recipe

Handle errors and reconnect in C

open62541's client keeps its own state machine. Call UA_Client_run_iterate in a loop, watch the channel and session state, and let the client re-establish rather than tearing down and rebuilding. Classify the status code before retrying.

Stackopen62541
Version1.5.8

The code

#include <open62541/client_config_default.h>
#include <open62541/client_highlevel.h>
#include <open62541/client_subscriptions.h>
#include <stdio.h>
#include <unistd.h>

static UA_Boolean isWorthRetrying(UA_StatusCode c) {
    switch (c) {
        case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED:
        case UA_STATUSCODE_BADUSERACCESSDENIED:
        case UA_STATUSCODE_BADSERVICEUNSUPPORTED:
            return false;   /* needs a human; retrying just floods the log */
        default:
            return true;
    }
}

int main(void) {
    UA_Client *client = UA_Client_new();
    UA_ClientConfig *cc = UA_Client_getConfig(client);
    UA_ClientConfig_setDefault(cc);
    cc->timeout = 5000;
    cc->requestedSessionTimeout = 60000;

    unsigned backoff = 1;

    while (1) {
        UA_SecureChannelState ch;
        UA_SessionState se;
        UA_StatusCode connectStatus;
        UA_Client_getState(client, &ch, &se, &connectStatus);

        if (se != UA_SESSIONSTATE_ACTIVATED) {
            UA_StatusCode rc = UA_Client_connect(client, "opc.tcp://opcua.umati.app:4840");
            if (rc != UA_STATUSCODE_GOOD) {
                printf("connect failed: %s\n", UA_StatusCode_name(rc));
                if (!isWorthRetrying(rc)) {
                    printf("permanent - needs configuration, giving up\n");
                    break;
                }
                sleep(backoff);
                backoff = backoff < 60 ? backoff * 2 : 60;   /* cap the backoff */
                continue;
            }
            printf("connected\n");
            backoff = 1;
        }

        /* drives reconnection, keep-alive and publish processing */
        UA_Client_run_iterate(client, 1000);
    }

    UA_Client_delete(client);
    return 0;
}

What it prints

connected
connect failed: BadConnectionClosed
connected
# with a bad certificate instead:
# connect failed: BadCertificateUntrusted
# permanent - needs configuration, giving up

Notes

  • UA_Client_getState gives you the channel state, session state and last connect status separately. All three matter when diagnosing a flapping connection.
  • UA_Client_run_iterate does the work: keep-alive, reconnection and publish processing. It is not optional.
  • Do not retry certificate or permission errors. They fail identically forever, and the retry loop buries the one log line that would have told you why.
  • Cap the backoff and add jitter across a fleet. Synchronised reconnects turn a network blip into an outage.
  • After reconnecting, confirm monitored items are still reporting. The client may have transferred the subscription, or it may have silently lost it.

What breaks first

An embedded client retries a permanent error forever and fills its flash with logs BadCertificateUntrusted
Classify status codes before retrying, as above. Configuration errors need an operator, not another attempt.

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