pentaho/pentaho-kettle · error · KettleException
JobGetMailsFromPOP.Error.MoveToIMAPFolderEmpty
JobGetMailsFromPOP.Error.MoveToIMAPFolderEmpty
Error message
JobGetMailsFromPOP.Error.MoveToIMAPFolderEmpty
What it means
During JobEntryGetPOP.execute() validation, when the protocol is IMAP and the action is MOVE (or the after-get action is MOVE), the resolved 'Move to IMAP folder' value is checked with Utils.isEmpty; if empty, a KettleException keyed 'JobGetMailsFromPOP.Error.MoveToIMAPFolderEmpty' is thrown. A target folder is mandatory for move operations.
Solutions
- Set the 'Move to IMAP folder' field to a valid existing IMAP folder (e.g. INBOX/Archive)
- If using a variable, confirm it is defined (set it in the job/kettle.properties) so substitution is non-empty
- Change the after-get action from MOVE to DELETE or NOTHING if moving is not intended
Example fix
// before
<move_to_imap_folder>${IMAP_ARCHIVE}</move_to_imap_folder> <!-- unset variable -->
// after
// define in kettle.properties: IMAP_ARCHIVE=INBOX/Archive
<move_to_imap_folder>INBOX/Archive</move_to_imap_folder> Defensive patterns
Strategy: validation
Validate before calling
// before executing the job entry, mimic its check:
String realFolder = folder == null ? null : folder.replaceAll("\\$\\{[^}]+\\}", "");
if (isMoveAction && (realFolder == null || realFolder.trim().isEmpty())) {
throw new IllegalArgumentException("MoveToIMAPFolder must be set for IMAP move actions");
} Try / catch
try { result = jobEntry.execute(prevResult, kannelTransMeta...); } catch (KettleException e) {
if (e.getMessage() != null && e.getMessage().contains("MoveToIMAPFolderEmpty")) {
log.error("Set the destination IMAP folder or change the after-get action");
}
} Prevention
- Always populate 'Move to IMAP folder' when selecting a move action on IMAP
- Verify any ${variables} in the folder field are defined in kettle.properties or a Set Variables entry
- Run the job entry's Validate button in Spoon before scheduling
When it happens
Trigger: JobEntryGetPOP (public execute) with protocol=IMAP and actionType=ACTION_TYPE_MOVE, or actionType=GET with afterGetIMap=AFTER_GET_IMAP_MOVE, while the (environment-substituted) MoveToIMAPFolder field is null or blank.
Common situations: User selected 'move mail' or 'delete after move' but left the destination folder blank; an environment variable like ${IMAP_ARCHIVE_FOLDER} is unset so the substitution yields an empty string.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- JobGetMailsFromPOP.Error.ReceivedDateSearchTermEmpty
- Mail.Error.MailDestinationEmpty
- At least one slave server is required to be present in…
- ChangeFileEncoding.Error.TargetFileIsEmpty
- DynamicSQLRow.Exception.SQLFieldNameEmpty
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/0466e9a0c736a627.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/email-messages/impl/src/main/java/org/pentaho/di/job/entries/getpop/JobEntryGetPOP.java:1077
try {
boolean usePOP3 = getProtocol().equals( MailConnectionMeta.PROTOCOL_STRING_POP3 );
boolean moveAfter = false;
int nbrMailToRetrieve =
usePOP3 ? ( getRetrievemails() == 2 ? Const.toInt( getFirstMails(), 0 ) : 0 ) : Const.toInt(
getFirstIMAPMails(), 0 );
String realOutputFolder = createOutputDirectory( JobEntryGetPOP.FOLDER_OUTPUT );
String targetAttachmentFolder = createOutputDirectory( JobEntryGetPOP.FOLDER_ATTACHMENTS );
// Check destination folder
String realMoveToIMAPFolder = environmentSubstitute( getMoveToIMAPFolder() );
if ( getProtocol().equals( MailConnectionMeta.PROTOCOL_STRING_IMAP )
&& ( getActionType() == MailConnectionMeta.ACTION_TYPE_MOVE )
|| ( getActionType() == MailConnectionMeta.ACTION_TYPE_GET
&& getAfterGetIMAP() == MailConnectionMeta.AFTER_GET_IMAP_MOVE ) ) {
if ( Utils.isEmpty( realMoveToIMAPFolder ) ) {
throw new KettleException( BaseMessages.getString( PKG, "JobGetMailsFromPOP.Error.MoveToIMAPFolderEmpty" ) );
}
moveAfter = true;
}
// check search terms
// Received Date
String realBeginDate;
switch ( getConditionOnReceivedDate() ) {
case MailConnectionMeta.CONDITION_DATE_EQUAL:
case MailConnectionMeta.CONDITION_DATE_GREATER:
case MailConnectionMeta.CONDITION_DATE_SMALLER:
realBeginDate = environmentSubstitute( getReceivedDate1() );
if ( Utils.isEmpty( realBeginDate ) ) {
throw new KettleException( BaseMessages.getString(
PKG, "JobGetMailsFromPOP.Error.ReceivedDateSearchTermEmpty" ) );
}
beginDate = df.parse( realBeginDate );
break;
case MailConnectionMeta.CONDITION_DATE_BETWEEN:View on GitHub (pinned to f3058517a1)