How to deal with Medusa Records in AL

What is a Medusa record you may ask, it’s totally not something I just made up 🙂 A Medusa record is something that you cannot look upon without crashing Business Central. But you better check the video to figure out the details 🙂

https://youtu.be/mqvBgXZhol8

In this video, Erik tackles a fascinating and rare problem in Business Central: what he calls “Medusa Records” — records that crash your entire session the moment you try to read them. Drawing from Greek mythology, where looking at Medusa turned you to stone, Erik demonstrates how certain corrupted or buggy records in virtual tables can kill your Business Central session on contact, and shows a clever technique to identify them without getting “turned to stone” yourself.

What Is a Medusa Record?

The concept comes from the old days of the native NAV database, where hardware failures or database corruption could produce records that would crash Dynamics NAV as soon as you tried to access them. While this is rare in the modern SQL-based world, it can still happen — particularly with virtual tables.

In this case, Erik encountered a bug in Business Central where certain records in the Report Data Items virtual table (a system table that describes the data items within reports) would crash the entire session upon access. This was particularly problematic because Erik’s Advanced Cloud Security app iterates through this table to determine where to apply security rules — and suddenly started dying without explanation.

The Problem: You Can’t Touch It

Erik first demonstrates the problem with the simplest possible code — just looping through the Report Data Items table:

var
    Medusa: Record "Report Data Items";
begin
    Medusa.FindSet();
    repeat
    until Medusa.Next() = 0;
end;

Running this crashes Business Central immediately. The debugger disconnects because the entire session dies. Even trying Medusa.Count produces the same result — as soon as you glance at the problematic record, you’re dead.

This rules out normal error handling approaches. You can’t use Codeunit.Run to catch the error because it’s not a regular AL error — it’s a session-killing crash. The session doesn’t get a chance to handle anything.

The Solution: Background Sessions as Canaries

Erik’s insight is brilliant in its simplicity: if touching the record kills the session, then run each check in a separate background session. If the session dies, so be it — your main session survives. The approach works like this:

  1. Loop through all report objects in the AllObj table (which is safe to read).
  2. For each report, start a background session that attempts to read its Report Data Items.
  3. Before attempting to read, insert a record into a custom tracking table marking the report as “Medusa” (dangerous) and commit it.
  4. If the session survives reading the data items, update the record to “No Medusa” (safe).
  5. If the session crashes, the committed “Medusa” status remains in the database — identifying the culprit.

The Tracking Table

First, Erik creates a simple table to track results:

table 51100 "Medusa"
{
    fields
    {
        field(1; ReportID; Integer)
        { }
        field(2; Status; Option)
        {
            OptionMembers = Medusa,"No Medusa";
        }
    }
    keys
    {
        key(PK; ReportID)
        { }
    }
}

The Medusa Detector Codeunit

The detector codeunit runs in the background session. It uses TableNo = AllObj so it can receive the report record. The key trick is the commit before the dangerous read:

codeunit 51100 "MedusaDetector"
{
    TableNo = AllObj;
    trigger OnRun()
    var
        RDI: Record "Report Data Items";
        Medusa: Record Medusa;
    begin
        // Step 1: Assume the worst - mark as Medusa and commit
        Medusa.Init();
        Medusa.ReportID := Rec."Object ID";
        Medusa.Status := Medusa.Status::Medusa;
        Medusa.Insert();
        Commit();

        // Step 2: Attempt the dangerous read
        RDI.Setrange("Report ID", Rec."Object ID");
        if RDI.findset() then
            repeat
            until RDI.Next() = 0;

        // Step 3: If we survived, update to safe
        Medusa.Status := Medusa.Status::"No Medusa";
        Medusa.Modify();
    end;
}

The Commit() after the insert is crucial. Because this runs in a background session, the commit ensures the “Medusa” status is persisted to the database. If the session crashes during the FindSet or Next loop, the committed record survives, permanently marking that report as dangerous. If the session completes successfully, the status is updated to “No Medusa.”

The Orchestrator

The main page extension loops through all reports and spawns a background session for each one:

pageextension 51100 CustomerListExt extends "Customer List"
{
    trigger OnOpenPage();
    var
        Reports: Record AllObj;
        SessionId: Integer;
        Medusa: Record Medusa;
    begin
        Medusa.DeleteAll();
        Reports.setrange("Object Type", Reports."Object Type"::Report);
        if Reports.FindSet() then
            repeat
                StartSession(SessionId, Codeunit::MedusaDetector, CompanyName, Reports);
            until Reports.Next() = 0;
    end;
}

Results and Practical Considerations

After running this process, Erik opens the Medusa table and finds that reports 1304 and 1305 contain Medusa records — their sessions crashed before they could update to “No Medusa.” Every other report shows “No Medusa,” meaning they were safely read.

Erik notes a few practical considerations:

  • Session limits: Spawning hundreds of background sessions rapidly can overwhelm the server. Adding a Sleep() call between session starts can help prevent this, especially in cloud sandboxes which may have stricter limits than local Docker containers.
  • Cloud vs. local: A Docker sandbox may be more forgiving about rapid session creation than a cloud sandbox.
  • Using the results: Once you’ve identified the Medusa records, you can instruct your code to skip those specific reports when iterating through the Report Data Items table, keeping your app alive while the underlying bug gets fixed.

Summary

This is a creative application of background sessions as “canary” processes — expendable sessions sent ahead to test whether a record is safe to read. The pattern of “assume guilty, commit, test, then acquit” is the key insight: by committing the dangerous status before attempting the read, you ensure that a session crash leaves behind evidence of exactly which record caused it. While Medusa records are rare in modern Business Central, having this technique in your toolkit is invaluable when you encounter session-killing bugs that can’t be caught through normal error handling.