I was under the impression that when I runReport() using the ReportManager class that a rolling 24 hour instance would be cached so that if I need the data in a subsequent call I would not need to rerun the report. But now as I am implementing my framework I'm not finding a method to get the instance ID that I was expecting.

Is this something only available through the REST API?

At the moment I'm attempting something like:

Reports.ReportResults Reports.ReportManager.runReport(reportId);

Thinking then that I could do something like data.getId() or data.getInstanceId(); but those aren't methods of the ReportResults class.

EDIT: I've since learned that I need to use Reports.ReportManager.runAsyncReport() in order to get a Reports.ReportInstance so that I can use the getId()

Now that I've fixed the syntax issue I'm not sure how to get the report that I need when I need it. Before using runReport() I had the results readily available but now using an async method I have to check the status like so:

public static Reports.ReportResults processInstance(Reports.ReportInstance reportInstance) {        
    if (reportInstance.getStatus() != 'Running' && reportInstance.getStatus() != 'New') {
        return reportInstance.getReportResults();
    } else {           
        // This does not work: causes Maximum stack depth reached: 1001 error
        return processInstance(reportInstance);

Since I cannot use my else statement above, and basically the report is never going to be ready on page load (visualforce page), so how do I get the results that I need. Is this something I have to use JS/Ajax somehow?


The reason the "else" call is failing is because you're calling your method recursively. The final recursion (when the report Status is no longer new or running) never happens so you blow the stack.

AFAIK, there is no time to "wait" in APEX until a certain event happens (or even sleep a thread for a certain amount of time). The reason they prevent you from doing that is so that you don't lock a thread in their runtime environment.

The only way I would see to do this is to have external, non-apex code (javascript?) polling your report until it's ready, and then displaying it.

  • 2
    or VF actionPoller – cropredy Dec 16 '15 at 17:16
  • I came to same conclusion and because <apex:poller> has a minimum of 5 seconds it won't work for my solution. For now running report synchronously on a scheduler and storing results in a cache object to be used at run time instead of running report at run time. Not sure how to get String stored back to a Reports.ReportResults data type though... :( – Xtremefaith Dec 16 '15 at 20:32

Below is how I managed to run the reports from Apex only if they are older than 2 hours. Otherwise, I would return a cached results without running them again. This helped me make sure I wouldn't run over my limits.

    List<Report> reportList = [SELECT Id, DeveloperName FROM Report WHERE DeveloperName='Monthly_Usage_Report'];
    String reportId = (String) reportList.get(0).get('Id');

    //get all the cached async ReportInstances performed against this report
    List<Reports.ReportInstance> repInstances = Reports.ReportManager.getReportInstances(reportId);

    //find the latest instance
    Reports.ReportInstance latestInstance;
    for(Reports.ReportInstance instance : repInstances){
        if(instance.getStatus() == 'Success'){
            latestInstance = instance;

    //see if the latest instance is fresh enough
    DateTime twoHoursAgo = DateTime.now().addHours(-2);
    Reports.ReportResults rResults;
    if(latestInstance != null && latestInstance.getCompletionDate() > twoHoursAgo){
        //this is fresh
        System.debug('cached report');
        rResults = latestInstance.getReportResults();
        //the last instance is not fresh, get the new one synchronously 
        //and run the async one so I have it cached for future calls
        System.debug('stale report, getting a fresh one');
        Reports.ReportInstance instance = Reports.ReportManager.runAsyncReport(reportId, true);     
        rResults = Reports.ReportManager.runReport(reportId, true);

    //do stuff with rResults

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.