# Handle errors

> How-to guide: When the client returns a result with a status and when it throws an OpcException, how to evaluate both, and which status codes are common.

> This content was machine translated and may contain errors. [Read in German](https://www.traeger.de/docs/development/opcua/client/error-handling)

The client distinguishes two kinds of errors:

- **Errors of single operations** are reported through a status in the result. A value read, the result of a write, or the result of a browse path each carries its own status. If one of many operations fails, the others still receive their result.
- **Errors of the entire call** are reported through an `OpcException`. This happens if there is no connection, the server rejects the request as a whole, or a method call fails.

## Evaluate the status

Types used: `OpcValue`, `OpcStatus`, `OpcStatusCode`

An `OpcStatus` divides results into three groups: `IsGood`, `IsUncertain`, and `IsBad`. Values with an uncertain status carry a value whose quality the server does not guarantee, for example the last usable value of a failed source. `Code` returns the exact `OpcStatusCode`, `Description` a readable description:

```csharp
OpcValue value = client.ReadNode("ns=2;s=Machine/Speed");
 
if (value.Status.IsGood)
    Console.WriteLine("Speed: {0}", value.Value);
else if (value.Status.IsUncertain)
    Console.WriteLine("Speed (uncertain): {0}", value.Value);
else
    Console.WriteLine("Read failed: {0} ({1})", value.Status.Code, value.Status.Description);

```

```vbnet
Dim value As OpcValue = client.ReadNode("ns=2;s=Machine/Speed")

If value.Status.IsGood Then
    Console.WriteLine("Speed: {0}", value.Value)
ElseIf value.Status.IsUncertain Then
    Console.WriteLine("Speed (uncertain): {0}", value.Value)
Else
    Console.WriteLine("Read failed: {0} ({1})", value.Status.Code, value.Status.Description)
End If
```

Writes return an `OpcStatus` or an `OpcStatusCollection` directly. Use `Code` to handle specific causes of errors:

```csharp
OpcStatus result = client.WriteNode("ns=2;s=Machine/Setpoint", value: 42.0);
 
if (result.Code == OpcStatusCode.BadUserAccessDenied)
    Console.WriteLine("The user is not allowed to write the setpoint.");
else if (result.IsBad)
    Console.WriteLine("Write failed: {0}", result.Description);

```

```vbnet
Dim result As OpcStatus = client.WriteNode("ns=2;s=Machine/Setpoint", value:=42.0)

If result.Code = OpcStatusCode.BadUserAccessDenied Then
    Console.WriteLine("The user is not allowed to write the setpoint.")
ElseIf result.IsBad Then
    Console.WriteLine("Write failed: {0}", result.Description)
End If
```

In calls with many commands, each result belongs to the command with the same index, see [Write many values in one call](https://www.traeger.de/en/docs/development/opcua/client/bulk-access#write-many-values-in-one-call).

## Handle exceptions

Types used: `OpcClient`, `OpcDiscoveryClient`, `OpcException`, `OpcStatusCode`

An `OpcException` carries the status code of the error in its `Code` property. Method calls throw it as soon as the server rejects the call or reports one of the input arguments as invalid:

```csharp
try {
    object[] outputs = client.CallMethod(
            "ns=2;s=Machine",
            "ns=2;s=Machine/StartJob",
            "Job 1");
}
catch (OpcException ex) {
    Console.WriteLine("Call failed: {0} ({1})", ex.Code, ex.Message);
}
```

```vbnet
Try
    Dim outputs As Object() = client.CallMethod( _
            "ns=2;s=Machine", _
            "ns=2;s=Machine/StartJob", _
            "Job 1")
Catch ex As OpcException
    Console.WriteLine("Call failed: {0} ({1})", ex.Code, ex.Message)
End Try

```

`Connect` also reports errors as an `OpcException`, for example if the server is not reachable or no endpoint matches the security settings of the client. The client then returns to its previous state:

```csharp
try {
    client.Connect();
}
catch (OpcException ex) {
    if (ex.Code == OpcStatusCode.BadSecurityChecksFailed)
        Console.WriteLine("No endpoint matches the security settings of the client.");
    else
        Console.WriteLine("Connect failed: {0}", ex.Code);
}
```

```vbnet
Try
    client.Connect()
Catch ex As OpcException
    If ex.Code = OpcStatusCode.BadSecurityChecksFailed Then
        Console.WriteLine("No endpoint matches the security settings of the client.")
    Else
        Console.WriteLine("Connect failed: {0}", ex.Code)
    End If
End Try
```

The same applies to `OpcDiscoveryClient`: if the server is not reachable, closes the channel while connecting, or the scheme of the discovery address is not supported, `DiscoverEndpoints`, `DiscoverServers`, and the constructors throw an `OpcException` stating the reason, for example `BadSecureChannelClosed`.

Besides `OpcException`, the usual .NET exceptions occur: `ArgumentNullException` for missing arguments, `InvalidOperationException` if a call is not possible in the current state (for example `Connect` without a server address), and `ObjectDisposedException` after `Dispose`. If the application calls a service while there is no connection, the client throws an `OpcException` with `BadServerNotConnected`.

## Common status codes

| Status code | Meaning | Typical cause |
|---|---|---|
| `BadNodeIdUnknown` | The node does not exist in the address space of the server. | wrong identifier or namespace index, see [Address nodes by their namespace URI](https://www.traeger.de/en/docs/development/opcua/client/read-write-values#address-nodes-by-their-namespace-uri) |
| `BadNodeIdInvalid` | The syntax of the identifier is invalid. | typo in the NodeId string |
| `BadNotReadable`, `BadNotWritable` | The access rights (AccessLevel) of the node do not allow reading or writing. | read-only variable |
| `BadUserAccessDenied` | The user of the session has no permission for the operation. | user identity with insufficient rights, see [Authenticate users](https://www.traeger.de/en/docs/development/opcua/security/client#authenticate-users) |
| `BadTypeMismatch` | The value written does not have the data type of the node. | for example `int` written instead of `double` |
| `BadOutOfRange` | The value is outside the permitted range. | limit of the server exceeded |
| `BadWriteNotSupported` | The server does not support the combination of value, status, and timestamps. | see [Timestamps when writing](https://www.traeger.de/en/docs/development/opcua/client/read-write-values#timestamps-when-writing) |
| `BadIndexRangeInvalid` | The syntax of the index range is invalid. | wrong `OpcRange` |
| `BadNoMatch` | A browse path cannot be resolved. | wrong path in `TranslatePaths` |
| `BadTooManyOperations` | The request contains too many operations. | your own limit set too high, see [How the SDK splits large requests](https://www.traeger.de/en/docs/development/opcua/client/bulk-access#how-the-sdk-splits-large-requests) |
| `BadEncodingLimitsExceeded` | The limits for encoding or decoding were exceeded. | very large values, see [Data transfer limits](https://www.traeger.de/en/docs/development/opcua/client/configuration#data-transfer-limits) |
| `BadTimeout` | The operation exceeded the timeout. | `OperationTimeout` too short or server overloaded |
| `BadServerNotConnected` | The client is not connected to the server. | call before `Connect` or during a connection break |
| `BadSecurityChecksFailed` | An error occurred during the security checks. | no matching endpoint or rejected certificate |
| `BadIdentityTokenRejected` | The server rejected the user identity. | wrong user name or wrong password |
| `BadArgumentsMissing`, `BadInvalidArgument` | Input arguments of a method call are missing or invalid. | number or types of the arguments do not match |
| `UncertainLastUsableValue` | The source of the value no longer updates it. | failed PLC behind the server |

## Status codes from the server side

A server developed with the OPC UA .NET SDK reports some status codes itself or because your code tells it to. The pages on server development describe the following cases:

| Status code | When the server reports it | More information |
|---|---|---|
| `BadUserAccessDenied` | A user logs on through an endpoint that is not granted for their access control entry. Also for too many simultaneous logon attempts, and for calls to certificate management through GDS whose conditions are not met. | [Restrict users to endpoints](https://www.traeger.de/en/docs/development/opcua/security/server#restrict-users-to-endpoints), [Limit logon attempts](https://www.traeger.de/en/docs/development/opcua/security/server#limit-logon-attempts), [Manage certificates with GDS](https://www.traeger.de/en/docs/development/opcua/security/gds-push#prerequisites) |
| `BadNotSupported` | An event handler of the request processing denies a request by setting the `Cancel` property of the event arguments to `true`. | [Validate requests](https://www.traeger.de/en/docs/development/opcua/server/request-monitoring#validate-requests) |
| `BadCommunicationError` | Your code reports it on a node if the PLC behind the server is not reachable. The node keeps its last value. | [Update values](https://www.traeger.de/en/docs/development/opcua/server/device-gateway#update-values) |
| `BadAggregateNotSupported` | A client requests an aggregate that the server does not support. | [Aggregated values and values at specific times](https://www.traeger.de/en/docs/development/opcua/server/historical-data#aggregated-values-and-values-at-specific-times) |
| `BadNoData` | A client reads a value at a time before which no raw value exists. | [Aggregated values and values at specific times](https://www.traeger.de/en/docs/development/opcua/server/historical-data#aggregated-values-and-values-at-specific-times) |
| `UncertainDataSubNormal` | While calculating an aggregate, the server skipped raw values with a bad status. | [Aggregated values and values at specific times](https://www.traeger.de/en/docs/development/opcua/server/historical-data#aggregated-values-and-values-at-specific-times) |
| `BadEncodingLimitsExceeded` | A value exceeds the limits that the server sets for data exchange. | [Data transfer limits](https://www.traeger.de/en/docs/development/opcua/server/configuration#data-transfer-limits) |
