Exceptions
Every exception the provider throws derives from NTiException, itself derived from DbException as the ADO.NET contract requires (reference: DbException). A generic catch (DbException) therefore catches everything NTi throws.
DbException
NTiException base of every NTi exception
NTiSqlException SQL error from the database server (negative SQLCODE)
NTiCommandException failed CL command or program call
NTiCommunicationException communication failure, broken connectionWhat throws what
| Exception | Thrown when | Specific properties |
|---|---|---|
NTiException |
a provider error outside the three derived categories: API misuse, an impossible conversion (for example requesting text in CCSID 65535), a failed MFA callback (the original cause is preserved in InnerException) |
those of DbException |
NTiSqlException |
the database server returns a negative SQLCODE | SqlCode, SqlState (5 character SQLSTATE), SecondLevelMessage (second level text when requested) |
NTiCommandException |
a CL command, a program call, or a service program call fails | ReturnCode (the command server's return code), Messages (the IBM i message stack, see below) |
NTiCommunicationException |
connection failure, timeout, unexpected end of stream, malformed frame, TLS error | IsTransient, always true. The faulty connection is broken and discarded, but a retry on a fresh connection can succeed (EF/Polly retry strategies) |
Starting with .NET 6, SqlState overrides DbException.SqlState, so the generic catch (DbException e) when (e.SqlState == "42704") works as is.
NTiCommandResult: the message stack
NTiCommandException.Messages carries the IBM i message stack (CPFxxxx, MCHxxxx...) returned by the command server. Each entry is an NTiCommandResult:
| Property | Content |
|---|---|
Id |
the message identifier (CPF2105, MCH0602, SQL0204...) |
Type |
the message type code |
Severity |
severity, 0 to 99 |
File |
message file |
Library |
library of the message file |
Text |
first level text |
SubstitutionData |
the message's substitution data |
Help |
second level text (help) |
The same stack is available on the success path in connection.Result (see NTiConnection).
ToString and StackTrace: no call stack
Across the whole hierarchy, StackTrace is empty and ToString() is rendered without a call stack. The shipped product is obfuscated, and the stack trace is not part of the diagnostic surface (v4 policy).
The actionable information is the message and the chain of inner causes (InnerException), which is always preserved.
Cancellation: the connection breaks by design
Cancelling the token of an in-flight operation throws OperationCanceledException carrying the caller's token (not an NTi exception) and breaks the connection by design. The in-flight frame is lost, and the connection is never reused or handed back to the pool.
Reopen the connection (or pick up another one from the pool) before continuing.
using System;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Aumerial.Data.Nti;
class ExceptionDemo
{
static async Task Main()
{
await using var connection = new NTiConnection("server=MYIBMI;user=MYUSER;password=MYPASSWORD");
await connection.OpenAsync();
try
{
await connection.ExecuteClCommandAsync("DLTLIB LIB(NOPE)");
}
catch (NTiCommandException e) // failed CL command or program call
{
Console.WriteLine($"Return code {e.ReturnCode}");
foreach (var m in e.Messages) // IBM i message stack
Console.WriteLine($"{m.Id} [{m.Severity}] {m.Text}");
}
try
{
using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM MYLIB.NOPE";
await using var reader = await command.ExecuteReaderAsync();
}
catch (NTiSqlException e) // SQL error: negative SQLCODE
{
Console.WriteLine($"SQLCODE {e.SqlCode}, SQLSTATE {e.SqlState}");
Console.WriteLine(e.SecondLevelMessage);
}
catch (DbException e) // generic net: every NTi exception derives from DbException
{
Console.WriteLine(e.Message); // ToString() and Message render without a stack
}
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
using var slow = connection.CreateCommand();
slow.CommandText = "CALL MYLIB.LONGPROC()";
await slow.ExecuteNonQueryAsync(cts.Token);
}
catch (OperationCanceledException) // cancellation: the connection breaks by design
{
// The in-flight frame is lost: reopen the connection before continuing.
}
}
}