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

  1. Set the 'Move to IMAP folder' field to a valid existing IMAP folder (e.g. INBOX/Archive)
  2. If using a variable, confirm it is defined (set it in the job/kettle.properties) so substitution is non-empty
  3. 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

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


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)