0

I execute a post install script and sometimes a weird error is happening:

Not Serializable: System.Version stack trace External entry point

I don't understand what is going on since looks like the package installation is successful, but there is a problem in post install script.

public without sharing class SecgpmPostInstall implements System.InstallHandler {
    public void onInstall(InstallContext context) {
        try {
            if (Check.noApp()) {
                System.enqueueJob(new Q(context));
            }
        } catch (Exception e) {
            MailUtils.send(
                context.installerId(),
                'Installation Failure' + context.organizationId(),
                ' e: ' + e.getMessage() + ' stack trace ' + e.getStackTraceString()
            );
        }
    }
}

2 Answers 2

1

System.enqueueJob, Database.executeBatch, System.schedule, and System.scheduleBatch, all take their Queueable, Database.Batchable, or Schedulable parameter and serializes it for later execution. You cannot include any non-serializable properties in such a class, or you'll end up with this error. This includes things like Database.SavePoint and Version. These exceptions will be thrown at the moment any of those methods are called.

To be clear, the problem is simply that Version itself is not serializable. Here's a unit test to demonstrate this.

public class InstallQ implements Queueable {
    Version version;
    public InstallQ(Version version) {
        this.version = version;
    }
    public void execute(QueueableContext context) {
        
    }
}

global class OnInstallB implements InstallHandler {
    global void onInstall(InstallContext context) {
        // Just using a plain Version object
        InstallQ q = new InstallQ(new Version(1,0));
        System.enqueueJob(q);
    } 
}

@isTest public class InstallQTest {
    @isTest public static void test() {
        Test.startTest();
        Test.testInstall(new OnInstallB(), null);
        Test.stopTest();
    }
}

System.SerializationException: Not Serializable: System.Version


Sometimes, when you run into situations like this, is best to try to isolate the actual cause. In the future, you should remember that Version cannot be serialized. If you need to track the previous version, you will need to provide the version data in some other kind of object (even a simple Map<String, Integer> would do just fine).

As proof that it's not the Queueable's fault, or even the InstallHandler's fault, you can try to serialize a Version directly:

JSON.serialize(new Version(1,0));

System.JSONException: Apex Type unsupported in JSON: System.Version

In general, if JSON.serialize can't serialize an object, there's a possibility that you can't include it in a Batchable, Queueable, or Schedulable class, either. Note that this isn't 100% accurate: You can't JSON.serialize an sObjectField, but you can include such a field in the state of asynchronous code.

Another test would be to set up a quick Visualforce page to see if you get any errors:

public class VersionControl {
    public version versionData { get; set; }
    public VersionControl() {
        versionData = new Version(1,1);
    }
}

<apex:page controller="versionControl">
    <apex:form>
    </apex:form>
</apex:page>

Not Serializable: System.Version

Odds are, if Visualforce can't serialize the data, neither can asynchronous code.


I find Version to be kind of an odd exception, because it seems that there shouldn't be anything special about why it can't be serialized. However, most classes like this are either blocked because they would be useless outside of the original context (like Database.Savepoint), or because their internal state has special properties that must be set, and serialization support would imply deserialization support, which could cause data to be injected.

Ultimately, while your solution was correct, the understanding of the problem was not. Hopefully, I've been able to provide some clarification on the general subject of the Not Serializable error, including System.SerializationException and System.JSONException, which both have the same common root cause: you cannot serialize some kinds of data because their internal state would be exposed, or because they would be useless outside of the current transaction context.

2
  • agree, you are right, the problem is with System.enqueueJob( method
    – Patlatus
    Sep 19, 2022 at 8:10
  • Might be worth mentioning that volatile attributes do not interfere with this process.
    – Phil W
    Sep 19, 2022 at 9:07
0

Update: I think sfdcfox is correct, and the problem is with System.enqueueJob( but not the constructor.


The problem happens only during upgrade. Since InstallContext contains Version previousVersion property, which is not serializable and the method System.enqueueJob( serializes Queueable instance, this error happens only when previous version is not null.

Instead, parameters of InstallContext can be passed directly to avoid this error

public without sharing class SecgpmPostInstall implements System.InstallHandler {
    public void onInstall(InstallContext context) {
        try {
            if (Check.noApp()) {
                System.enqueueJob(new Q(context.installerId(), context.organizationId()));
            }
        } catch (Exception e) {
            MailUtils.send(
                context.installerId(),
                'Installation Failure' + context.organizationId(),
                ' e: ' + e.getMessage() + ' stack trace ' + e.getStackTraceString()
            );
        }
    }
}
1
  • I would think it more an issue in the Queueable's constructor's behaviour and any state held in non-volatile attributes of your Queueable. I doubt strongly this is simply because you passed the context to the constructor. The serialisation is done by the System.enqueueJob, not inherently by the construction of the queueable. You would find this if you split that line of code into two.
    – Phil W
    Sep 18, 2022 at 21:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .