+ Start a Discussion
Roger WickiRoger Wicki 

Visualfore error message causes internal server error

Dear community

I changed a bit of my visualforce controller to make it a bit more logically structured and to fix an undesired behaviour. Doing this kind of broke the whole thing, because now I'm getting an internal server error message...

Here is my VF page:
<apex:page standardController="Opportunity" id="createInstallments" showHeader="false" sidebar="false" extensions="InstallmentCreator" action="{!autoRun}" docType="html-5.0" cache="false">
<apex:pageMessages />
    <apex:form id="inputForm">
        <apex:pageBlock title="Installment Creation" id="pageBlock">
            <apex:pageBlockButtons id="pageBlockButtons">
                <apex:commandButton id="submit" action="{!submit}" value="Submit" title="Submit" disabled="!{!isSubmitAvailable}" />
                <apex:commandButton id="cancel" action="{!cancel}" value="Cancel" title="Cancel" />
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1" id="sectionOne">
                 The Amount to be split into rates is {!Opportunity.CurrencyIsoCode} {!Opportunity.Amount_Total__c}.<br/><br/>
                <apex:input type="number" id="numInstallments" label="Number of installments" value="{!numInstallments}" required="true"/>
                <apex:input type="number" id="avgAmount" label="Average installment amount" value="{!avgAmount}" required="true"/>
                <apex:input type="date" id="firstDueDate" label="First due date" value="{!firstDueDate}" required="true"/>
            </apex:PageBlockSection>
            <apex:PageBlockSection id="sectionTwo" columns="2">
                <apex:input type="number" id="interval" label="Interval" value="{!interval}"/>
                <apex:selectList value="{!intervalType}" size="1" label="Interval Type">
                    <apex:selectOption itemValue="Day" itemLabel="Day(s)"/>
                    <apex:selectOption itemValue="Month" itemLabel="Month(s)"/>
                </apex:selectList>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
It is meant to fetch Opportunity Data upon opening and display it right away (action={!autoRun}). Additionally, if data is in a certain form already, it should automatically process the creation of an opportunity's child object instead of showing the page. The page is accessed by a button click on Opportunity.

Here is my controller extension:
/**
*	@description
*	This class serves as a controller for the corresponding Visualforce Page "Create Installments". The class is intended to be called by a button click
*	on the UI. It is used to create Installment records out of the provided opportunity.
*	
*	@author	wicki
*/
public class InstallmentCreator
{
	public Opportunity opp {get; set;}
	public Date firstDueDate {get; set;}
	public Integer interval {get; set;}
	public String intervalType {get; set;}
	public Integer numInstallments {get; set;}
	public Decimal avgAmount {get; set;}
	public Boolean isSubmitAvailable {get; set;}
	private list<Installment__c> installments;
	private Boolean showError = false;
	private String opportunityId;
		
	/**
	*   In case this Controller is invoked, there is a constructor defined
	*/
	public InstallmentCreator(ApexPages.StandardController controller) {
		opp = (Opportunity)controller.getRecord();
	}
	
	/**
	*	@description	Called by the visualforce page
	*	@return			A page reference to either the page itself, displaying error messages or the calling page
	*/
	public PageReference autoRun() {
		opportunityId = (String)ApexPages.currentPage().getParameters().get('id');
		system.debug(LoggingLevel.ERROR, 'Opportunity Id: ' + opportunityId);
		if ( opportunityId == NULL || opportunityId.equals('') ) {
			return ApexPages.currentPage();
		}
		opp = [ SELECT Id, Payment_Information__c, PayableUntil__c, StageName, InvoiceDate__c, Amount_Total__c, CurrencyIsoCode
     			 FROM Opportunity WHERE Id = :opportunityId LIMIT 1 ];
//		validateAutoRun();
		if ( showError ) {
			return ApexPages.currentPage();
		}
		
// Comment out on using rates
		numInstallments = 1;
		avgAmount = opp.Amount_Total__c;
		intervalType = 'Day';
		interval = 0;
		firstDueDate = (opp.Payment_Information__c.equals('Payable due in advance') || (opp.Payment_Information__c.equals('Rates') && opp.PayableUntil__c == NULL)) ?
			system.today() : opp.PayableUntil__c;
		
		return submit();
	}

	/**
	*	@description	Validates input data and creates Installments if data is valid
	*	@return			A Page Reference to the page itself if the data is invalid or to the calling page if everything went right
	*/
	public PageReference submit() {
		validateSubmit();
		if ( showError ) {
			return ApexPages.currentPage();
		} else {
			createInstallments();
		}
		
		if ( showError ) {
			return ApexPages.currentPage();
		} else {
			return cancel();
		}
	}
	
	public PageReference cancel() {
		PageReference pageRef = new PageReference('/' + opp.Id);
		pageRef.setRedirect(true);
		return pageRef;
	}
	
	private void validateAutoRun() {
		String errorMessage = 'The opportunity does not fulfill all requirements to create Installments!';
		isSubmitAvailable = true;
		if ( !opp.StageName.contains('yes') ) {
			isSubmitAvailable = false;
			errorMessage += '\nStage must be yes!';
		}
		if ( opp.InvoiceDate__c == null ) {
			isSubmitAvailable = false;
			errorMessage += '\nThere has to be an invoice date!';
		}
		if ( opp.Payment_Information__c.equals('Date') && opp.PayableUntil__c == NULL ) {
			isSubmitAvailable = false;
			errorMessage += '\nIf the payment is for a specific date, the date has to be provided!';
		}
		if ( !isSubmitAvailable ) {
			showError = true;
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, errorMessage));
		}
	}
	
	private void validateSubmit() {
		if ( opp.Amount_Total__c > numInstallments * avgAmount || numInstallments < 1 || avgAmount < 0 || avgAmount > opp.Amount_Total__c || firstDueDate == NULL ) {
			isSubmitAvailable = false;
			showError = true;
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,
				'The number of Installments and the average amount will not produce anything useful. Please use other values!'));
		}
	}
	
	private void createInstallments() {
		installments = new list<Installment__c>();
		Integer installmentNum = 1;
		Decimal oppAmount = opp.Amount_Total__c;
		
		while ( (oppAmount > 0.0 || opp.Amount_Total__c == 0.0) && installmentNum <= numInstallments ) {
			Installment__c installment = new Installment__c();
			installment.Name = '' + installmentNum;
			installment.OpportunityId__c = opp.Id;
			installment.CurrencyIsoCode = opp.CurrencyIsoCode;
			if ( intervalType.equals('Day') ) {
				installment.DueOn__c = firstDueDate.addDays((installmentNum - 1) * interval);
			} else if ( intervalType.equals('Month') ) {
				installment.DueOn__c = firstDueDate.addMonths((installmentNum - 1) * interval);
			} else {
				isSubmitAvailable = false;
				showError = true;
				ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'There was an error with the Interval Type selection.'));
			}
			installment.Amount__c = Math.min(avgAmount, oppAmount);
			oppAmount -= avgAmount;
			installmentNum++;
			installments.add(installment);
		}
		try {
			insert installments;
		} catch(system.DMLException dml) {
			isSubmitAvailable = false;
			showError = true;
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Could not insert Installments.\n' + dml));
			system.debug(LoggingLevel.ERROR, 'Could not insert Installments.\n' + dml);
		}
	}
}
The thing is, I want the user to be shown my VF page posted above if the data which is already present is not sufficient. Also, if the page is shown and the page is submitted (future feature) the page should still stay open and show the relevant error messages.


Here is the currently working controller extension:
/**
*  @description
*  This class serves as a controller for the corresponding Visualforce Page "Create Installments". The class is intended to be called by a button click
*  on the UI. It is used to create Installment records out of the provided opportunity.
*  
*  @author  wicki
*/
public class InstallmentCreator
{
  public Opportunity opp {get; set;}
  public Date firstDueDate {get; set;}
  public Integer interval {get; set;}
  public String intervalType {get; set;}
  public Integer numInstallments {get; set;}
  public Decimal avgAmount {get; set;}
  public Boolean isSubmitAvailable {get; set;}
  private list<Installment__c> installments;
  private Boolean isConstructorCalled = false;
  
  /**
  *   In case this Controller is invoked, there is a constructor defined
  */
  public InstallmentCreator(ApexPages.StandardController controller) {
    this.opp = (Opportunity)controller.getRecord();
    isConstructorCalled = true;
  }
  
  /**
  *   Called by the visualforce page 
  */
  public PageReference autoRun() {
    String opportunityId = ApexPages.currentPage().getParameters().get('id');
 
    if (opportunityId == null && !isConstructorCalled) {
      // Display the Visualforce page's content if no Id is passed over
      return ApexPages.CurrentPage();
    } else if ( isConstructorCalled ) {
      opportunityId = opp.Id;
    }
    opp = [ SELECT Id, Payment_Information__c, PayableUntil__c, StageName, InvoiceDate__c, Amount_Total__c, CurrencyIsoCode
      FROM Opportunity WHERE Id = :opportunityId LIMIT 1 ];
    validateAutoRun();
    
// Comment out on using rates
    numInstallments = 1;
    avgAmount = opp.Amount_Total__c;
    intervalType = 'Day';
    interval = 0;
    firstDueDate = (opp.Payment_Information__c.equals('Payable due in advance') || (opp.Payment_Information__c.equals('Rates') && opp.PayableUntil__c == NULL)) ?
      system.today() : opp.PayableUntil__c;
    return submit();
    
// Uncomment on using rates
/*    if ( !opp.Payment_Information__c.equals('Rates') ) {
      numInstallments = 1;
      avgAmount = opp.Amount_Total__c;
      intervalType = 'Day';
      interval = 0;
      firstDueDate = opp.Payment_Information__c.equals('Payable due in advance') ? system.today() : opp.PayableUntil__c;
      return submit();
    }
*/  }
  
  public PageReference submit() {
    validateSubmit();
    createInstallments();
    
    // Redirect the user back to the original page
    PageReference pageRef = new PageReference('/' + opp.Id);
    pageRef.setRedirect(true);
    return pageRef;
  }
  
  public PageReference cancel() {
    PageReference pageRef = new PageReference('/' + opp.Id);
    pageRef.setRedirect(true);
    return pageRef;
  }
  
  private void validateAutoRun() {
    String errorMessage = 'The opportunity does not fulfill all requirements to create Installments!';
    isSubmitAvailable = true;
    if ( !opp.StageName.contains('yes') ) {
      isSubmitAvailable = false;
      errorMessage += '\nStage must be yes!';
    }
    if ( opp.InvoiceDate__c == null ) {
      isSubmitAvailable = false;
      errorMessage += '\nThere has to be an invoice date!';
    }
    if ( opp.Payment_Information__c.equals('Date') && opp.PayableUntil__c == NULL ) {
      isSubmitAvailable = false;
      errorMessage += 'If the payment is for a specific date, the date has to be provided!';
    }
    if ( !isSubmitAvailable ) {
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, errorMessage));
    }
  }
  
  private void validateSubmit() {
    if ( opp.Amount_Total__c > numInstallments * avgAmount || numInstallments < 1 || avgAmount < 0 || avgAmount > opp.Amount_Total__c ) {
      isSubmitAvailable = false;
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,
        'The number of Installments and the average amount will not produce anything useful. Please use other values!'));
    }
  }
  
  private void createInstallments() {
    installments = new list<Installment__c>();
    Integer installmentNum = 1;
    Decimal oppAmount = opp.Amount_Total__c;
    
    while ( (oppAmount > 0.0 || opp.Amount_Total__c == 0.0) && installmentNum <= numInstallments ) {
      Installment__c installment = new Installment__c();
      installment.Name = '' + installmentNum;
      installment.OpportunityId__c = opp.Id;
      installment.CurrencyIsoCode = opp.CurrencyIsoCode;
      if ( intervalType.equals('Day') ) {
        installment.DueOn__c = firstDueDate.addDays((installmentNum - 1) * interval);
      } else if ( intervalType.equals('Month') ) {
        installment.DueOn__c = firstDueDate.addMonths((installmentNum - 1) * interval);
      } else {
        isSubmitAvailable = false;
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'There was an error with the Interval Type selection.'));
      }
      installment.Amount__c = Math.min(avgAmount, oppAmount);
      oppAmount -= avgAmount;
      installmentNum++;
      installments.add(installment);
    }
    try {
      insert installments;
    } catch(system.DMLException dml) {
      isSubmitAvailable = false;
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Could not insert Installments.\n' + dml));
      system.debug(LoggingLevel.ERROR, 'Could not insert Installments.\n' + dml);
    }
  }
}

The error I'm getting is:
An internal server error has occurred
An error has occurred while processing your request. The salesforce.com support team has been notified of the problem. If you believe you have additional information that may be of help in reproducing or correcting the error, please contact Salesforce Support. Please indicate the URL of the page you were requesting, any error id shown on this page as well as any other related information. We apologize for the inconvenience. 

Thank you again for your patience and assistance. And thanks for using salesforce.com! 

Error ID: 499064766-251065 (-1373969722)


I have no clue how I can get rid of this error. I tried uncommenting single lines to see where the error might be. I also monitored my user and got the following debug log:
 

35.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,NONE;DB,INFO;SYSTEM,INFO;VALIDATION,INFO;VISUALFORCE,NONE;WORKFLOW,ERROR
14:03:19.124 (124061938)|EXECUTION_STARTED
14:03:19.124 (124085918)|CODE_UNIT_STARTED|[EXTERNAL]|066250000000OcX|VF: /apex/CreateInstallments
14:03:19.127 (127236448)|CODE_UNIT_STARTED|[EXTERNAL]|01p250000009e82|InstallmentCreator <init>
14:03:19.127 (127256520)|SYSTEM_MODE_ENTER|true
14:03:19.155 (155103094)|CODE_UNIT_FINISHED|InstallmentCreator <init>
14:03:19.155 (155234067)|CODE_UNIT_STARTED|[EXTERNAL]|01p250000009e82|InstallmentCreator invoke(autoRun)
14:03:19.155 (155622692)|USER_DEBUG|[34]|ERROR|Opportunity Id: 00625000004COln
14:03:19.155 (155987042)|SOQL_EXECUTE_BEGIN|[38]|Aggregations:0|SELECT Id, Payment_Information__c, PayableUntil__c, StageName, InvoiceDate__c, Amount_Total__c, CurrencyIsoCode FROM Opportunity WHERE Id = :tmpVar1 LIMIT 1
14:03:19.162 (162498577)|SOQL_EXECUTE_END|[38]|Rows:1
14:03:19.163 (163507077)|VF_PAGE_MESSAGE|The number of Installments and the average amount will not produce anything useful. Please use other values!
14:03:19.163 (163584357)|CODE_UNIT_FINISHED|InstallmentCreator invoke(autoRun)
14:03:19.164 (164312516)|VF_APEX_CALL|createInstallments|{!autoRun}|PageReference:/apex/CreateInstallments?id=00625000004COln&scontrolCaching=1
14:03:19.165 (165970977)|CUMULATIVE_LIMIT_USAGE
14:03:19.165 (165970977)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 1 out of 100
  Number of query rows: 1 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

14:03:19.165 (165970977)|CUMULATIVE_LIMIT_USAGE_END

14:03:19.166 (166018172)|CODE_UNIT_FINISHED|VF: /apex/CreateInstallments
14:03:19.167 (167136200)|EXECUTION_FINISHED
Best Answer chosen by Roger Wicki
Roger WickiRoger Wicki
I finally found the root cause for the internal server error. For some reason I changed the return value away from what I copied the idea from (Invoke Apex from a Custom Button (http://sfdc.arrowpointe.com/2009/01/08/invoke-apex-from-a-custom-button-using-a-visualforce-page/)). Initially to show the actual visualforce page, a return value of null was used. I changed that to ApexPages.currentPage(), which broke the entire page.

I found out by commenting out everything and started retyping my controller extension from scratch and testing after every bit. Changing the return type of the autoRun method to PageReference and returning a non-null value brough the ISE.

All Answers

pconpcon
Whenever you get an Internal Server Error (ISE) like this, there are two ways to determine what is causing this.  The way that requires the least amount of work for you is to contact support and provide the Error Id.  If you don't have support you will have to do something like a binary search to figure out what part of your page / controller / etc is causing the exception.  This is teadious and time consuming but it's really the only other way to know.  You can limit your search by looking at where the exception occurs (when your debug log stops).  Looking at your debug log, it may have something to do with the apex:messages tag or the error you are adding to it.
Roger WickiRoger Wicki
Hi pcon

I feared I'd get this kind of answer, but well, then I'll really have to continue searching like this. We do not have premium support so I'll have to check by trial & error.

For your input with apex:messages: I thought so too but I don't really know why it only happens after altering my code. Because the one I have running in production does never throw any such kind of error. On the other hand the currently running version has no chance of even displaying my VF page due to the bad logic I used... I rather think the problem lies in a VF field, that I set to required which would be null with the data I tried it with (although it fails with others as well).

I'll post the source of the problem resp. the solution for anyone finding this error with no clue where to start.
Roger WickiRoger Wicki
I finally found the root cause for the internal server error. For some reason I changed the return value away from what I copied the idea from (Invoke Apex from a Custom Button (http://sfdc.arrowpointe.com/2009/01/08/invoke-apex-from-a-custom-button-using-a-visualforce-page/)). Initially to show the actual visualforce page, a return value of null was used. I changed that to ApexPages.currentPage(), which broke the entire page.

I found out by commenting out everything and started retyping my controller extension from scratch and testing after every bit. Changing the return type of the autoRun method to PageReference and returning a non-null value brough the ISE.
This was selected as the best answer