pentaho/pentaho-kettle · error · FileSystemException

Illegal move target

Error message

Illegal move target

What it means

JCRSolutionFileObject.callRepoMove refuses to move a file when the target folder is null, which happens when the computed destination lies beyond the repository root. It throws a plain FileSystemException('Illegal move target') before any repository call is made. This is a client-side guard against a malformed move destination.

Solutions

  1. Validate that the target's parent resolves to an existing folder inside the repository before calling moveTo
  2. Skip or reject moves whose target parent is null (beyond root) instead of attempting them
  3. Compute the target via another valid FileObject rather than string-manipulating paths

Example fix

// before
FileObject dest = fs.resolveFile( parentPathOf( src ) ); // may be beyond root
src.moveTo( dest );
// after
FileObject dest = fs.resolveFile( parentPathOf( src ) );
if ( dest == null || dest.getParent() == null ) {
  throw new IllegalArgumentException( "Move target must be inside the repository" );
}
src.moveTo( dest );
Defensive patterns

Strategy: validation

Validate before calling

// validate before moveTo
boolean isValidMoveTarget( FileObject target ) throws FileSystemException {
  if ( target == null ) return false;
  FileObject parent = target.getParent();
  return parent != null && parent.exists();
}

Type guard

FileObject requireInsideRepository( FileObject target ) throws FileSystemException {
  FileObject parent = ( target == null ) ? null : target.getParent();
  if ( parent == null ) throw new IllegalArgumentException( "Move target must be inside the repository" );
  return target;
}

Try / catch

try {
  src.moveTo( target );
} catch ( FileSystemException e ) {
  if ( "Illegal move target".equals( e.getMessage() ) ) {
    // reject operation in UI/log instead of crashing
  } else { throw e; }
}

Prevention

When it happens

Trigger: Calling fileObject.moveTo(target) where resolving the target's parent yields null — i.e. attempting to move a folder (typically a top-level folder) to a parent above the repository root.

Common situations: Programmatic renames/moves that compute the destination path by stripping a path segment, accidentally producing a root-level or above-root target; UI drag-and-drop moving a top-level folder onto the root.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/fa93da496d8e2033. Report an issue: GitHub.

Appendix: source

Thrown at plugins/repo-vfs/repo-vfs-ws/src/main/java/org/pentaho/di/plugins/repovfs/ws/vfs/JCRSolutionFileObject.java:353

    return Objects.equal( file1.getName().getExtension(), file2.getName().getExtension() );
  }

  private static String getNameNoExt( FileObject file ) {
    return FilenameUtils.removeExtension( file.getName().getBaseName() );
  }

  /** Rename this file in place */
  private void callRepoRename( String newName ) throws RepositoryClientException {
    String[] origPath = computeFileNames( getName() );
    log.debug( "Optimized rename call" );
    repoClient.rename( origPath, newName );
  }

  /** Move this to a different parent */
  private void callRepoMove( FileObject targetFolder ) throws FileSystemException, RepositoryClientException {
    if ( targetFolder == null ) {
      // beyond root
      throw new FileSystemException( "Illegal move target" );
    }
    if ( !targetFolder.exists() ) {
      log.debug( "Creating target folder {} for move", targetFolder.getName() );
      targetFolder.createFolder();
    }
    RepositoryFileDto thisFile = getFileDto();
    String[] destPath = computeFileNames( targetFolder.getName() );
    log.debug( "Optimized move call" );
    repoClient.moveTo( thisFile, destPath );
  }

  private static boolean haveSameName( FileObject file1, FileObject file2 ) {
    return file1.getName().getBaseName().equals( file2.getName().getBaseName() );
  }

  private static boolean haveSameParent( FileObject file1, FileObject file2 ) throws FileSystemException {
    FileObject parent1 = file1.getParent();
    FileObject parent2 = file2.getParent();

View on GitHub (pinned to f3058517a1)