{"record":{"id":"ff2563d1c80bc2c9","repo":"pentaho/pentaho-kettle","slug":"destination-must-be-a-folder-invalid-drag-drop-source","errorCode":null,"errorMessage":"Destination must be a folder / invalid drag-drop source (dynamic message via processErrorMessage)","messagePattern":"Destination must be a folder / invalid drag-drop source \\(dynamic message via processErrorMessage\\)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"plugins/file-open-save-new/core/src/main/java/org/pentaho/di/plugins/fileopensave/dragdrop/ElementDndProcessor.java","lineNumber":104,"sourceCode":"\n  private static File determineDestinationFolder( Element destination, VariableSpace variableSpace ) {\n    if ( checkArray( destination.getEntityType(), DESTINATION_BLACKLIST ) ) {\n      processErrorMessage( \"Invalid destination.  The destination cannot be a \" + destination.getEntityType() );\n    }\n    return destination.convertToFile( variableSpace );\n  }\n\n  private static File[] determineSourceObjects( Element[] source, VariableSpace variables ) {\n    return Arrays.stream( source ).map( x -> x.convertToFile( variables ) ).filter( Objects::nonNull )\n      .toArray( File[]::new );\n  }\n\n  private static boolean checkArray( EntityType entityType, EntityType[] array ) {\n    return Arrays.stream( array ).filter( x -> entityType.equals( x ) ).findFirst().isPresent();\n  }\n\n  private static void processErrorMessage( String errorMessage ) {\n    throw new IllegalArgumentException( errorMessage );\n  }\n\n  private static void checkIfDestinationIsChildOfSource( File[] sourceObjects, File destinationFolder ) {\n    if ( !destinationFolder.getEntityType().isDirectory() ) {\n      processErrorMessage( \"Destination \\\"\" + destinationFolder.getPath() + \"\\\" must be a folder.\" );\n    }\n    for ( File source : sourceObjects ) {\n      int sourcePathLength = source.getPath().length();\n      if ( destinationFolder.getPath().indexOf( source.getPath() ) == 0 && ( destinationFolder.getPath().length() ==\n        sourcePathLength\n        || \"/\\\\\".indexOf( destinationFolder.getPath().substring( sourcePathLength, sourcePathLength + 1 ) ) != -1 ) ) {\n        processErrorMessage(\n          \"Destination folder \\\"\" + destinationFolder.getPath() + \"\\\" cannot be a sub-folder of source \\\"\"\n            + source.getPath() + \"\\\".  This can cause infinite looping.\" );\n      }\n    }\n  }\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/plugins/file-open-save-new/core/src/main/java/org/pentaho/di/plugins/fileopensave/dragdrop/ElementDndProcessor.java#L86-L122","documentation":"ElementDndProcessor.processErrorMessage throws IllegalArgumentException with a dynamic message when a drag-and-drop target is invalid — typically the destination is not a folder ('Destination \"...\" must be a folder.') or the destination is a child of the source.","triggerScenarios":"Dragging files onto a destination that is a file rather than a directory (checked in determineDestinationFolder / checkIfDestinationIsChildOfSource), or nesting a folder into its own descendant.","commonSituations":"Dragging onto a file icon by accident; attempting to move a parent folder into its own subtree in the file-open-save dialog.","solutions":["Drop onto a directory, not a file","Avoid moving a folder into one of its own children","Catch IllegalArgumentException from the DnD handler and show a validation message instead of performing the move"],"exampleFix":"// before\nprocessErrorMessage( \"Destination \\\"\" + destinationFolder.getPath() + \"\\\" must be a folder.\" );\n// after (caller-side guard)\nif ( destinationFolder.getEntityType().isDirectory() ) { processor.move( src, dest ); }","handlingStrategy":"validation","validationCode":"if ( !destinationFolder.getEntityType().isDirectory() ) {\n  throw new IllegalArgumentException( \"Destination must be a folder\" );\n}\nif ( isChildOf( destinationFolder, sourceObjects ) ) {\n  throw new IllegalArgumentException( \"Destination is child of source\" );\n}","typeGuard":null,"tryCatchPattern":"try { dndProcessor.determineDestinationFolder( source, target ); } catch ( IllegalArgumentException e ) { showValidationError( e.getMessage() ); }","preventionTips":["UI: only enable drop on directory nodes","Disable drops on nodes inside the dragged subtree","Show target entity type before drop"],"tags":["drag-and-drop","validation","file-dialog","illegal-argument"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}