Substring Error - Ending Position Out Of Bounds

Note that you do not need to skip an index between substrings. Your current pattern actually loses data. When you do substring(2001, 4000), you start at character #2002. When you do substring(0, 2000), you stop at character #2000 (whose index was 1999). So you lose character #2001 whose index is 2000.

This can be demonstrated simply:

String text = '1234567890';
system.assertEquals('12345', text.substring(0, 5);
system.assertEquals('7890', text.substring(6, 10);

From the documentation (emphasis mine):

  • substring(startIndex, endIndex)
    Returns a new String that begins with the character at the specified zero-based startIndex and extends to the character at endIndex - 1.

If instead the substring extended to endIndex, you would need to do substring(0, 1999), etc.


Solution

You need to split the data out into up to 16 chunks. To make it scalable, I would do something like the following in an Apex Class and then call it from your trigger.

public with sharing class CaseServices
{
    static final List<SObjectField> responseChunkFields = new List<SObjectField>
    {
        Case.Response_Part1__c, Case.Response_Part2__c,
        Case.Response_Part3__c, Case.Response_Part4__c,
        Case.Response_Part5__c, Case.Response_Part6__c,
        Case.Response_Part7__c, Case.Response_Part8__c,
        Case.Response_Part9__c, Case.Response_Part10__c,
        Case.Response_Part11__c, Case.Response_Part12__c,
        Case.Response_Part13__c, Case.Response_Part14__c,
        Case.Response_Part15__c, Case.Response_Part16__c,
    };
    public static void chunkResponses(List<Case> records)
    {
        for (Case record : records) chunkResponse(record);
    }
    static void chunkResponse(Case record)
    {
        if (String.isBlank(record.Response__c)) return;
        Integer length = record.Response__c.length;
        Integer chunks = (Integer)(length/2000.0).round(RoundingMode.UP);
        for (Integer i = 0; i < chunks; i++)
        {
            Integer j = (i == chunks - 1) ? length : 2000*(i+1);
            String chunk = record.Response__c.substring(2000*i, j);
            record.put(responseChunkFields[i], chunk);
        }
    }
}

As an alternative to using substring, you can also use Pattern/Matcher:

SObjectField[] fields = new SObjectField[] {  // List your fields...
    Case.Response_Part1__c, Case.Response_Part2__c,
    Case.Response_Part3__c, Case.Response_Part4__c // ...
};
Pattern p = Pattern.compile('(?s).{1,2000}');  // Read up to 2k at a time

for(Case record: Trigger.new) {
    if(record.Response__c == null || record.Response__c == Trigger.oldMap.get(record.Id).Response__c) {
        continue;
    }
    Matcher m = p.matcher(c.Response__c);
    for(Integer fieldToUse = 0; fieldToUse < fields.size() && m.find(); fieldToUse++) {
        record.put(fields[fieldToUse], m.group(0));
    }
}

This avoids substring exceptions and a particularly nasty set of if statements.


Reason :-

The length of string is less than 2000 which caused an error.

Resolution:-

Please use length() function of String class to solve the issue. Please use the below code:-

trigger UpdateCaseResponse on Case (before update) {        
    for(Case c: trigger.new ){        
        Integer resLength =  c.Subject.length();        
        if (c.Response__c != null)        
        if(c.Response__c != Trigger.oldMap.get(c.Id).Response__c )            
        if( resLength <= 2000){
            c.Response_Part1__c = c.Response__c.substring(0,resLength);
        }                       
        else if( resLength > 2000 &&  resLength <= 4000 ){              
            c.Response_Part1__c = c.Response__c.substring(0,2000);
            c.Response_Part2__c = c.Response__c.substring(2001,resLength);
        }                               
    }  
}