Exceptions

Every exception thrown by the provider derives from NTiException, which derives from DbException as the ADO.NET contract requires: a generic catch (DbException) therefore catches everything NTi (DbException reference).

DbException
  NTiException                  base of every NTi exception
    NTiSqlException             SQL error from the database server (negative SQLCODE)
    NTiCommandException         CL command or program call failure
    NTiCommunicationException   communication failure, broken connection

Which condition throws what

Exception Thrown when Specific properties
NTiException a provider error outside the three derived categories: API misuse, impossible conversion (for example requesting text in CCSID 65535), a failing MFA callback (original cause preserved as 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 (command server return code); Messages (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, a new attempt on a fresh connection may succeed (EF/Polly retry strategies)

On net6 and later, SqlState overrides DbException.SqlState: generic code such as 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 message identifier (CPF2105, MCH0602, SQL0204...)
Type message type code
Severity severity, 0 to 99
File message file
Library message file library
Text first level text
SubstitutionData substitution data of the message
Help second level (help) text

The same stack is available on the success side through 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 call stacks are not part of the diagnostic surface (v4 policy). The actionable information is the message and the chain of inner causes (InnerException), always preserved.

Cancellation: the connection breaks by contract

Cancelling the token of an in-flight operation throws OperationCanceledException carrying the CALLER's token (not an NTi exception) and breaks the connection BY CONTRACT: the in-flight frame is lost, the connection is never reused nor returned to the pool. Reopen the connection (or take a fresh 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)                    // CL or program failure
        {
            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: everything NTi derives from DbException
        {
            Console.WriteLine(e.Message);                // ToString() and Message are rendered 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: connection broken by contract
        {
            // The in-flight frame is lost: reopen the connection before continuing.
        }
    }
}

Reconnecting to the server...

The connection to the server was lost. The page will reload.