How to handle multiple errors in a single catch block and display the error message in X++ for Dynamics 365 F&O

Hello everyone,

I am working with X++ in Dynamics 365 Finance & Operations and need help with error handling. I have a block of code where I’m performing multiple operations inside a while loop, and each operation might throw an error. I want to catch all errors with a single catch block and print the error message that is returned from the try block.

Here is my current code: (By the way, the table can contain more than 6 million records.)

try
{
while select tmpItemTable
{
str itemBarCodeStr;
str SectorfamilyCode;
str Sectorfamily;
str EcoResCategoryCode;
real PxVenteStandard;
real PxTarif;
real PxAchatNet;

    try
    {
        itemBarCodeStr = strFmt("%1", tmpItemTable.ItemBarCode);
    }
    catch
    {
        error("Error in Operation 1 of item file");
        throw Exception::error;
    }

    try
    {
        SectorfamilyCode = subStr(tmpItemTable.SectorfamilyCode, 1, 2);
    }
    catch
    {
        error("Error in Operation 2 of item file");
        throw Exception::error;
    }

    try
    {
        Sectorfamily = subStr(tmpItemTable.Sectorfamily, 1, 50);
    }
    catch
    {
        error("Error in Operation 3 of item file");
        throw Exception::error;
    }

    try
    {
        EcoResCategoryCode = subStr(tmpItemTable.EcoResCategoryCode, 1, 6);
    }
    catch
    {
        error("Error in Operation 4 of item file");
        throw Exception::error;
    }

    try
    {
        PxVenteStandard = roundDowndec(tmpItemTable.PxVenteStandard, 2);
    }
    catch
    {
        error("Error in Operation 5 of item file");
        throw Exception::error;
    }

    try
    {
        PxTarif = roundDowndec(tmpItemTable.PxTarif, 2);
    }
    catch
    {
        error("Error in Operation 6 of item file");
        throw Exception::error;
    }

    try
    {
        PxAchatNet = roundDowndec(tmpItemTable.PxAchatNet, 2);
    }
    catch
    {
        error("Error in Operation 7 of item file");
        throw Exception::error;
    }

    try
    {
        io.writeExp([
            parameters.Code_Soc,
            tmpItemTable.Brand,
            tmpItemTable.ItemName,
            tmpItemTable.designation,
            tmpItemTable.produitcompose,
            tmpItemTable.gereenstock,
            sectorfamilyCode,
            sectorfamily,
            tmpItemTable.ecorescategorycodeparent,
            tmpItemTable.ecorescategorynameparent,
            EcoResCategoryCode,
            tmpItemTable.ecorescategoryname,
            "",
            tmpItemTable.ecorescategoryremisename,
            "",
            PxVenteStandard,
            "",
            "n",
            "",
            itemBarCodeStr,
            PxTarif,
            "",
            PxAchatNet,
            "",
            "",
            "",
            tmpItemTable.typearticle,
            tmpItemTable.SCAStockStrategy
        ]);
    }
    catch
    {
        error("Error in io.writeExp of item file");
        throw Exception::error;
    }
}

}
catch
{
error(“Error in while loop tmpItemTable”);
throw Exception::error;
}

Issue:

I don’t know, but I always face the issue where the code hits this error: ‘Error in io.writeExp of item file’. Since I haven’t found a way to display the error message coming from the try block, I just added a message to identify the source, but I don’t know the exact error.

  • By the way, the scenario is that I’m trying to extract data from D365 F&O. I’m using insert record set and update record set to implement data into a table, since we have more than 6 million rows. Then, I transform the regular table into a temporary table to iterate over it. I face the same error when I loop over the regular table. When I get to the io.write, it sometimes works, but most of the time, I run into this error. I don’t know exactly what causes the error because I haven’t found a way to display the error message from the try block. I just added a message to help me identify where the issue occurs, but I’m still unsure about the exact error

The problem I’m facing is that I want to use just one catch block to handle all errors in the try block and print the error message that is returned from the try block (especially for the errors in io.writeExp). But sometimes the errors do not return any message, and I can’t seem to capture them correctly.

Solution I’m Looking For:

  • I need a way to catch all errors with a single catch block and print the message associated with the error , even when some errors don’t return a message.
  • I want to avoid multiple nested try-catch blocks and have just one catch for the entire operation.

Could someone help me find a way to achieve this with one catch block that catches all errors and prints the appropriate error message?

Thanks in advance!

First of all, let me simplify your code post in the right way. Both will make it easier to read.

public void run()
{
    try
    {
        while select tmpItemTable
        {
            this.processItem(tmpItemTable);
        }
    }
    catch
    {
        throw error(“Error in while loop tmpItemTable”);
    }
}

private void processItem(TmpItemTable tmpItemTable)
{
    try
    {
        itemBarCodeStr = strFmt("%1", _tmpItemTable.ItemBarCode);
    }
    catch
    {
        throw error("Error in Operation 1 of item file");
    }

    str sectorFamilyCode;

    try
    {
        sectorFamilyCode = subStr(_tmpItemTable.SectorfamilyCode, 1, 2);
    }
    catch
    {
        throw error("Error in Operation 2 of item file");
    }

    str sectorFamily;

    try
    {
        sectorFamily = subStr(_tmpItemTable.Sectorfamily, 1, 50);
    }
    catch
    {
        throw error("Error in Operation 3 of item file");
    }

    str ecoResCategoryCode;

    try
    {
        ecoResCategoryCode = subStr(_tmpItemTable.EcoResCategoryCode, 1, 6);
    }
    catch
    {
        throw error("Error in Operation 4 of item file");
    }

    real pxVenteStandard;

    try
    {
        pxVenteStandard = roundDowndec(_tmpItemTable.PxVenteStandard, 2);
    }
    catch
    {
        throw error("Error in Operation 5 of item file");
    }

    real pxTarif;

    try
    {
        pxTarif = roundDownDec(_tmpItemTable.PxTarif, 2);
    }
    catch
    {
        throw error("Error in Operation 6 of item file");
    }
    
    real pxAchatNet;

    try
    {
        pxAchatNet = roundDownDec(_tmpItemTable.PxAchatNet, 2);
    }
    catch
    {
        throw error("Error in Operation 7 of item file");
    }

    try
    {
        io.writeExp([
            parameters.Code_Soc,
            _tmpItemTable.Brand,
            _tmpItemTable.ItemName,
            _tmpItemTable.Designation,
            _tmpItemTable.ProduitCompose,
            _tmpItemTable.GereenStock,
            sectorFamilyCode,
            sectorFamily,
            _tmpItemTable.EcoResCategoryCodeParent,
            _tmpItemTable.EcoResCategoryNameParent,
            ecoResCategoryCode,
            _tmpItemTable.EcoResCategoryName,
            '',
            _tmpItemTable.EcoResCategoryRemiseName,
            '',
            pxVenteStandard,
            '',
            'n',
            '',
            itemBarCodeStr,
            pxTarif,
            '',
            pxAchatNet,
            '',
            '',
            '',
            _tmpItemTable.TypeArticle,
            _tmpItemTable.SCAStockStrategy
        ]);
    }
    catch
    {
        throw error("Error in io.writeExp of item file");
    }
}

Now please tell us more about your original problem: I haven’t found a way to display the error message coming from the try block. Are you saying that you’re able to catch an exception but it doesn’t add anything to infolog and you don’t know how to look at properties of the exceptions?

It seems that your questions like how to use a single catch block aren’t really relevant. You can easily use a single catch block by removing all the nested ones. Your actual problem is getting the error message.

Are you able to reproduce the problem? If so, we can throw away all other code and focus just one the problem.

1 Like

Thanks @MartinDrab for your reply. Yes, you’re right—the issue isn’t really about using a single catch block; I can simplify that easily. The real problem is that although the exception is being caught, I don’t know how to properly access or display the error message inside the catch block.

I’m getting this error during execution:
Error in io.writeExp of item file
It seems to originate from this call to io.writeExp:

io.writeExp([
parameters.Code_Soc,
tmpItemTable.Brand,
tmpItemTable.ItemName,
tmpItemTable.designation,
tmpItemTable.produitcompose,
tmpItemTable.gereenstock,
sectorfamilyCode,
sectorfamily,
tmpItemTable.ecorescategorycodeparent,
tmpItemTable.ecorescategorynameparent,
EcoResCategoryCode,
tmpItemTable.ecorescategoryname,
“”,
tmpItemTable.ecorescategoryremisename,
“”,
PxVenteStandard,
“”,
“n”,
“”,
itemBarCodeStr,
PxTarif,
“”,
PxAchatNet,
“”,
“”,
“”,
tmpItemTable.typearticle,
tmpItemTable.SCAStockStrategy
]);

But in the catch block, I can’t seem to get the actual error message text. Could you let me know how to extract or log the exception details properly in X++?

Yes, I’m able to reproduce the problem consistently, but i can’t display it in the catch block as info(message come from try Block), because i don’t know how to do it , if there is a possibility to do it, it will be good to know the problem come from in the try

The first thing I would is looking at the exception object in the debugger. There is an automatically created variable that you can use to check the exception type and its properties, such as the message and stack trace.
ErrorException
That will tell you what information is available. If there is no message, you can’t do anything about it.

Note that you can also catch the exception object and analyze it in your code. You may be interested in my blog post Catching exceptions in AX 7, where I showed things like that.

1 Like

Hello @MartinDrab , :waving_hand:Thankyou for your support !!
I’m sharing my code with you because I’m really struggling to understand why the extraction fails at the io.writeExp line. I suspect it might be due to the large amount of data being processed, but I haven’t been able to pinpoint the exact issue.

The scenario is that we needs to extract millions of rows from D365 F&O. To handle this, I created a class that uses insert_recordset and update_recordset to efficiently insert data into a regular table. Each extraction is tagged with a unique extractionID. Afterward, I move the data into a temporary table for looping and output generation.

The problem is that the process consistently fails at this line:

x++

CopyEdit

error("Error in io.writeExp of item file");

Oddly, it sometimes works, which makes it even harder to diagnose. I’m unsure how to properly trace where the error is coming from or why it happens inconsistently.

I’d really appreciate your help, as I’m currently stuck on this., i will share with you the code in two part because it is so long

using System.Threading.Tasks;
class DataExtraction
{
DataExtractionParameters parameters;
public void init()
{
boolean valid = true;
parameters = DataExtractionParameters::find();
if(parameters.param1 == “”)
{
valid = checkFailed(‘Msg : ’+" "+ ‘EmptyField’);
}
if(!valid)
{
throw warning(‘ErrorMsg’);
}
}


public void fileExtraction(str sendFileToUser)
{
    
    container                           lineData;
    TextStreamIo                        io;
    System.IO.Stream                    stream;
    System.IO.StreamReader              reader;
    XML                                 fileContent;
    container                           fileHeader = this.intiHeader();
    io= TextStreamIo::constructForWrite();
    io.outFieldDelimiter(";");
    io.write(fileHeader);
    VendTable                           vendTable;
    SALESPARAMETERS                     salesParameters;
    ItemTable                   ItemTable;
    
    HandleUploadFailure handleUploadFailure = new HandleUploadFailure();
    TmpItemTable                     tmpItemTable;
    Query query;
    QueryRun queryRun;
    QueryBuildDataSource qbds;
    QueryBuildRange qbr;
    str uniqueId;
    boolean isUnique = false;
    GenerateUniqueId uniqueIdProvider = new GenerateUniqueId();
    while (!isUnique)
    {
        uniqueId = uniqueIdProvider.generateUniqueId();
        if (!this.isUniqueIdExists(uniqueId))
        {
            isUnique = true;
        }
    }

    try
    {
        insert_recordset ItemTable (ItemId,ItemName,Brand,Product,StockStrategy,primaryVendorId,ExtractionSessionId)
           select ItemId,  NameAlias,Brand ,Product,StockStrategy,primaryVendorId,uniqueId
           from inventTable;
    }
    catch
    {
        error("Error in block 1 of item file");
        throw Exception::error;
    }

    try
    {
        update_recordset ItemTable
        setting Designation = ecoResProductTranslation.Name
        where ItemTable.ExtractionSessionId == uniqueId
        join Name,Product,LanguageId from ecoResProductTranslation
        where ecoResProductTranslation.Product == ItemTable.Product
        && ecoResProductTranslation.LanguageId == languageId;
    }
    catch
    {
        error("Error in block 2 of item file");
        throw Exception::error;
    }

    try
    {
        insert_recordset tmpItemTable (
        ItemBarCode,
        SectorfamilyCode,
        Sectorfamily,
        EcoResCategoryCode,
        PxVenteStandard,
        PxTarif,
        PxAchatNet,
        Brand,
        ItemName,
        designation,
        produitcompose,
        gereenstock,
        ecorescategorycodeparent,
        ecorescategorynameparent,
        ecorescategoryname,
        ecorescategoryremisename,
        typearticle,
        StockStrategy
        )
        select
        ItemBarCode,
        SectorfamilyCode,
        Sectorfamily,
        EcoResCategoryCode,
        PxVenteStandard,
        PxTarif,
        PxAchatNet,
        Brand,
        ItemName,
        designation,
        produitcompose,
        gereenstock,
        ecorescategorycodeparent,
        ecorescategorynameparent,
        ecorescategoryname,
        ecorescategoryremisename,
        typearticle,
        StockStrategy from ItemTable where ItemTable.ExtractionSessionId == uniqueid;
    }
    catch
    {
        error("Error in block 38 of item file");
        throw Exception::error;
    }
  

  
    try
    {
        while select tmpItemTable
        {
            str itemBarCodeStr;
            str SectorfamilyCode;
            str Sectorfamily;
            str EcoResCategoryCode;
            real PxVenteStandard;
            real PxTarif;
            real PxAchatNet;

            try
            {
                itemBarCodeStr = strFmt("%1",tmpItemTable.ItemBarCode);
            }
            catch
            {
                error("Error in Operation 1 of item file");
                throw Exception::error;
            }

            try
            {
                SectorfamilyCode = subStr(tmpItemTable.SectorfamilyCode,1,2);
            }
            catch
            {
                error("Error in Operation 2 of item file");
                throw Exception::error;
            }


            try
            {
                Sectorfamily = subStr(tmpItemTable.Sectorfamily,1,50);
            }
            catch
            {
                error("Error in Operation 3 of item file");
                throw Exception::error;
            }

            try
            {
                EcoResCategoryCode = subStr(tmpItemTable.EcoResCategoryCode,1,6);
            }
            catch
            {
                error("Error in Operation 4 of item file");
                throw Exception::error;
            }

            try
            {
                PxVenteStandard = roundDowndec(tmpItemTable.PxVenteStandard,2);
            }
            catch
            {
                error("Error in Operation 5 of item file");
                throw Exception::error;
            }

            try
            {
                PxTarif = roundDowndec(tmpItemTable.PxTarif,2);
            }
            catch
            {
                error("Error in Operation 6 of item file");
                throw Exception::error;
            }

            try
            {
                PxAchatNet = roundDowndec(tmpItemTable.PxAchatNet,2);
            }
            catch
            {
                error("Error in Operation 7 of item file");
                throw Exception::error;
            }

            try
            {
                io.writeExp([
                    parameters.Code_Soc,
                    tmpItemTable.Brand,
                    tmpItemTable.ItemName,
                    tmpItemTable.designation,
                    tmpItemTable.produitcompose,
                    tmpItemTable.gereenstock,
                    sectorfamilycode,
                    sectorfamily,
                    tmpItemTable.ecorescategorycodeparent,
                    tmpItemTable.ecorescategorynameparent,
                    ecorescategorycode,
                    tmpItemTable.ecorescategoryname,
                    "",
                    tmpItemTable.ecorescategoryremisename,
                    "",
                    pxventestandard,
                    "",
                    "n",
                    "",
                    itembarcodestr,
                    pxtarif,
                    "",
                    pxachatnet,
                    "",
                    "",
                    "",
                    tmpItemTable.typearticle,
                    tmpItemTable.SCAStockStrategy
                    ]);
            }
            catch
            {
                error("Error in io.writeExp of item file");
                throw Exception::error;
            }
        }
    }
    catch
    {
        error("Error in while loop tmpItemTable");
        throw Exception::error;
    }


    try
    {
        stream = io.getStream();
    }
    catch
    {
        error("Error in io.getStream of item file");
        throw Exception::error;
    }

    try
    {
        stream.Position = 0;
    }
    catch
    {
        error("Error in stream.Position of item file");
        throw Exception::error;
    }

    try
    {
        reader = new System.IO.StreamReader(stream);
    }
    catch
    {
        error("Error in StreamReader of item file");
        throw Exception::error;
    }

    try
    {
        fileContent = reader.ReadToEnd();
    }
    catch
    {
        error("Error in ReadToEnd of item file");
        throw Exception::error;
    }

str fileName=‘FF.csv’;
if(sendFileToUser == “O”)
{
try
{
File::SendStringAsFileToUser(fileContent, fileName,System.Text.Encoding::UTF8);
}
catch
{
error(‘Error occurred while sending the file to the user.’);
throw Exception::Error;
}
}
else
{
if(parameters.SFTPCheckbox == NoYes::Yes)
{
SendFileToSFTP sftpUploader = new SendFileToSFTP();
str result =sftpUploader.sendFileToServer(
stream,
fileName,
parameters.SFTPServerName,
parameters.SFTPUserName,
parameters.SFTPUserPass,
parameters.SFTPFilesPath,
parameters.SFTPPort);

            if(result != "success")
            {
                error('ErrorMsg');
                handleUploadFailure.handleUF(stream,fileName);
                throw Exception::Error;
            }
        }
        else
        {
            UploadFileToSharepoint sharePointUploader = new UploadFileToSharepoint();
            str result = sharePointUploader.uploadFileToSharePoint(
                fileContent,
                parameters.tenantId,
                parameters.clientId,
                parameters.clientSecretId,
                parameters.driveId,
                parameters.SPFilesPath,
                fileName);

            if(result != "success")
            {
                error('ErrorMsg.');
                handleUploadFailure.handleUF(stream,fileName);
                throw Exception::Error;
            }
        }
    }
}


public boolean isUniqueIdExists(str _uniqueId)
{
ItemTable itemTable;
SalesParameters SalesParameters;



    boolean isExists = false;
    
    select firstOnly RecId from itemTable where itemTable.ExtractionSessionId == _uniqueId;
    if (itemTable.RecId != 0)
    {
        return true;
    }
    
    select firstOnly RecId from SalesParameters where SalesParameters.ExtractionSessionId == _uniqueId;
    if (SalesParameters.RecId != 0)
    {
        return true;
    }
    
    return isExists;
}

public container intiHeader()
{
    container       fileHeader;
    fileHeader = [
                    'Field1',
                     'Field2',
                      'Field3',
                      ...
                  ];
    return fileHeader;
}

public static void main(Args _args)
{
    str sendFileToUser = "O";
    DataExtraction    data = new DataExtraction();
    data.init();
    data.fileExtraction(sendFileToUser);
}


}

I’m sorry, but I don’t see how I could help you. If you can’t give us code that reproduce the problem with io.writeExp(), then you’re the only one who can look at what error you’re getting.

By the way, are you sure that the whole idea is correct? If you want to extract million rows, shouldn’t you export the data to something like Azure Data Lake?

To share code in this forum, put ``` on the line above and below and below your piece of code.

1 Like

Hello @MartinDrab,
I hope you’re doing well. I forgot to include the triple backticks (```) in the code. I’ve now modified it, and it’s divided into two parts.
However, I can’t use Azure—it’s not available in the list of options. The only option available to me is X++.

My question is:
How can I print the error message from an exception thrown in the try {} block, without specifying the exact type of exception?
Since I don’t know the type of exception that might be thrown, I want to use a generic catch block that catches any error and displays its message.

Is this the correct and only way to do it?

System.Exception ex
try {
    // Code that might throw an unknown type of error
}
catch (ex) {
    info(ex.Message);
}

Yes, that a generic code to show a message of an exception. But it shouldn’t be needed; the message should be shown automatically.

More specific exception types may have important information in other properties, not just in the message.

Note that your code has a dangerous side-effect: it handles the exception. Therefore code execution will continue despite of the error. You can use throw; to re-throw the exception.