Please ensure Javascript is enabled for purposes of website accessibility
Powered by Zoomin Software. For more details please contactZoomin

AVEVA™ Integration Service

Error Handling

  • Last UpdatedJul 28, 2026
  • 4 minute read

This section covers the exception types in the SDK, recommended handling patterns, retry strategies, and best practices.

Custom Exception Types

The SDK defines six custom exceptions, all in AVEVA.IntegrationService.DataAPI.SDK.Exceptions:

HubConnectionInitializationException

Thrown when the SignalR hub connection cannot be established.

try

{

var pubSubClient = await SignalRHubConnectionFactory.CreatePubSubClient(

httpClientFactory, hubConnectionManager, authType, host);

}

catch (HubConnectionInitializationException ex)

{

Console.WriteLine($"Failed to connect to SignalR hub: {ex.Message}");

// Check: host URL, network connectivity, authentication

}

DataPostMessageNotReceivedException

Thrown when a published message is not acknowledged by the hub.

try

{

var result = await pubSubClient.Publish(message, host, topic);

}

catch (DataPostMessageNotReceivedException ex)

{

Console.WriteLine($"Message not delivered: {ex.Message}");

// Consider retrying or checking hub status

}

EmptyDataObjectRecievedException

Thrown when an API call succeeds but returns an empty result set unexpectedly.

try

{

var data = await client.GetTableData("DataSource", "Table", filter);

}

catch (EmptyDataObjectRecievedException ex)

{

Console.WriteLine($"No data returned: {ex.Message}");

// Verify datasource name, table name, and filter

}

MethodNotApplicableException

Thrown when a method is called on a client/datasource type that does not support it. Most commonly seen when using IDataApiClient (generic client) with datasource-specific methods.

try

{

var result = await client.PostTableData("DataSource", "Table", data, "");

}

catch (MethodNotApplicableException ex)

{

Console.WriteLine($"Operation not supported for this datasource type: {ex.Message}");

}

InternalPubSubClientException

Thrown for internal errors within the Pub/Sub client infrastructure.

try

{

await pubSubClient.Subscribe("Topic");

}

catch (InternalPubSubClientException ex)

{

Console.WriteLine($"Internal Pub/Sub error: {ex.Message}");

// Log and consider rebuilding the pub/sub client

}

SignaRConectionFailedException

Thrown when the SignalR connection drops or cannot be maintained.

try

{

await pubSubClient.Subscribe("Topic");

}

catch (SignaRConectionFailedException ex)

{

Console.WriteLine($"SignalR connection failed: {ex.Message}");

// Implement reconnection logic

}

Handling Patterns

Catch Specific Exception Types

Always prefer specific catches over a blanket Exception catch:

var client = DataApiClientFactory.CreateDataApiClient<EngineeringClient>(host);

try

{

var data = await client.GetDataSources("");

// process data

}

catch (HubConnectionInitializationException ex)

{

_logger.LogError(ex, "Hub connection failed – check connectivity");

}

catch (EmptyDataObjectRecievedException ex)

{

_logger.LogWarning(ex, "No data returned from API");

}

catch (TimeoutException ex)

{

_logger.LogWarning(ex, "Request timed out");

}

catch (Exception ex)

{

_logger.LogError(ex, "Unexpected error calling DataAPI");

throw; // Re-throw if you cannot handle it here

}

Conditional Retry on Transient Exceptions

public async Task<IEnumerable<DatasourceDto>> GetDataSourcesResilient(string host)

{

int maxRetries = 3;

var client = DataApiClientFactory.CreateDataApiClient<EngineeringClient>(host);

for (int attempt = 1; attempt <= maxRetries; attempt++)

{

try

{

return await client.GetDataSources("");

}

catch (TimeoutException) when (attempt < maxRetries)

{

await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));

}

catch (HubConnectionInitializationException) when (attempt < maxRetries)

{

await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)));

}

catch (Exception ex)

{

_logger.LogError(ex, "Unrecoverable error on attempt {Attempt}", attempt);

throw;

}

}

throw new InvalidOperationException($"Operation failed after {maxRetries} attempts");

}

Retry Strategies

Manual Exponential Backoff

public async Task<T> ExecuteWithRetry<T>(Func<Task<T>> operation, int maxRetries = 3)

{

for (int attempt = 1; attempt <= maxRetries; attempt++)

{

try

{

return await operation();

}

catch (Exception ex) when (IsTransient(ex) && attempt < maxRetries)

{

var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt));

_logger.LogWarning(ex, "Attempt {Attempt} failed, retrying in {Delay}s", attempt, delay.TotalSeconds);

await Task.Delay(delay);

}

}

throw new InvalidOperationException($"Operation failed after {maxRetries} attempts");

}

private static bool IsTransient(Exception ex)

=> ex is TimeoutException || ex is HubConnectionInitializationException;

Polly Retry + Circuit Breaker

using Polly;

var retryPolicy = Policy

.Handle<TimeoutException>()

.Or<HubConnectionInitializationException>()

.WaitAndRetryAsync(

retryCount: 3,

sleepDurationProvider: attempt => TimeSpan.FromSeconds(Math.Pow(2, attempt)),

onRetry: (outcome, timespan, retryCount, context) =>

{

Console.WriteLine($"Retry {retryCount} after {timespan.TotalSeconds}s due to {outcome.Exception?.GetType().Name}");

}

);

var circuitBreaker = Policy

.Handle<Exception>()

.CircuitBreakerAsync(handledEventsAllowedBeforeBreaking: 3,

durationOfBreak: TimeSpan.FromMinutes(1));

var policy = Policy.WrapAsync(retryPolicy, circuitBreaker);

var data = await policy.ExecuteAsync(async () =>

await client.GetDataSources("")

);

Simple Manual Circuit Breaker

private int _errorCount = 0;

private DateTime _lastErrorTime = DateTime.MinValue;

private const int MaxErrors = 5;

private const int ResetMinutes = 5;

public async Task<T> ExecuteWithCircuitBreaker<T>(Func<Task<T>> operation)

{

if (_errorCount >= MaxErrors &&

DateTime.UtcNow < _lastErrorTime.AddMinutes(ResetMinutes))

{

throw new InvalidOperationException("Circuit breaker open – retry later.");

}

try

{

var result = await operation();

_errorCount = 0;

return result;

}

catch (Exception ex)

{

_errorCount++;

_lastErrorTime = DateTime.UtcNow;

_logger.LogError(ex, "Error count: {ErrorCount}", _errorCount);

throw;

}

}

Logging Errors

Structured Logging with Serilog

using Serilog;

try

{

var data = await client.GetTableData(datasource, table, filter);

}

catch (Exception ex)

{

Log.Error(ex,

"Failed to retrieve table data {DataSource}.{Table} with filter {Filter}",

datasource, table, filter);

}

Log with Context (ILogger)

_logger.LogError(ex,

"Operation={Operation} Host={Host} Attempt={Attempt} Elapsed={Elapsed}ms",

"GetDataSources", host, attempt, stopwatch.ElapsedMilliseconds);

Log Full Exception Chain

catch (Exception ex)

{

_logger.LogError(

"Type={Type} Message={Message} Stack={Stack}",

ex.GetType().Name, ex.Message, ex.StackTrace);

if (ex.InnerException != null)

_logger.LogError("InnerException={Inner}", ex.InnerException.Message);

}

HTTP Status Codes

Handling Status Codes

var response = await httpClient.GetAsync(url);

switch (response.StatusCode)

{

case System.Net.HttpStatusCode.OK:

var content = await response.Content.ReadAsStringAsync();

break;

case System.Net.HttpStatusCode.Unauthorized:

_logger.LogError("Authentication failed");

throw new InvalidOperationException("Invalid credentials");

case System.Net.HttpStatusCode.Forbidden:

throw new UnauthorizedAccessException("Not authorised to access this resource");

case System.Net.HttpStatusCode.NotFound:

throw new InvalidOperationException("Resource not found");

case System.Net.HttpStatusCode.RequestTimeout:

case System.Net.HttpStatusCode.ServiceUnavailable:

throw new InvalidOperationException("Service unavailable – retry later");

default:

response.EnsureSuccessStatusCode();

break;

}

Timeout Handling

Recognise Timeout Errors

try

{

var data = await client.GetDataSources("");

}

catch (TimeoutException ex)

{

Console.WriteLine("Request timed out");

}

catch (TaskCanceledException ex)

{

Console.WriteLine("Request was cancelled (likely due to timeout)");

}

catch (OperationCanceledException ex)

{

Console.WriteLine("Operation was cancelled");

}

Increase Timeout

var client = DataApiClientFactory.CreateDataApiClient<EngineeringClient>(

host: host,

authType: AuthenticationType.NTLM,

waitingTimeInMinutesForLiveData: 300 // 5 hours

);

Custom Timeout with CancellationToken

using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));

try

{

await client.GetDataSources().ConfigureAwait(false);

}

catch (OperationCanceledException)

{

Console.WriteLine("Operation timed out after 5 minutes");

}

Best Practices

Catch Specific Exceptions

// Good

catch (TimeoutException ex) { /* handle */ }

catch (HubConnectionInitializationException ex) { /* handle */ }

// Avoid

catch (Exception ex) { /* too broad */ }

Log Before Rethrowing

catch (Exception ex)

{

_logger.LogError(ex, "Operation failed");

throw; // Preserves the original stack trace

}

Add Context to Exceptions

catch (Exception ex)

{

throw new InvalidOperationException(

$"Failed to fetch '{table}' from '{datasource}'", ex);

}

Use Async All the Way

// Good

var data = await client.GetDataSources("");

// Avoid – blocks thread and wraps exceptions in AggregateException

var data = client.GetDataSources("").Result;

Never Swallow Exceptions Silently

// BAD – hides errors

catch (Exception) { }

// GOOD – at minimum, log it

catch (Exception ex)

{

_logger.LogError(ex, "Operation failed silently");

}

In This Topic
Related Links