{"record":{"id":"f36ca06d517f11c9","repo":"pentaho/pentaho-kettle","slug":"failed-to-delete","errorCode":null,"errorMessage":"Failed to delete: ","messagePattern":"Failed to delete: ","errorType":"exception","errorClass":"SftpException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSftpSession.java","lineNumber":133,"sourceCode":"  @Override\n  public void mkdir( String path ) throws SftpException {\n    try {\n      client.mkdir( path );\n    } catch ( IOException e ) {\n      throw new SftpException( \"Failed to create directory: \" + path, e );\n    }\n  }\n\n  @Override\n  public void delete( String path ) throws SftpException {\n    try {\n      if ( isDirectory( path ) ) {\n        client.rmdir( path );\n      } else {\n        client.remove( path );\n      }\n    } catch ( IOException e ) {\n      throw new SftpException( \"Failed to delete: \" + path, e );\n    }\n  }\n\n  @Override\n  public void rename( String oldPath, String newPath ) throws SftpException {\n    try {\n      client.rename( oldPath, newPath );\n    } catch ( IOException e ) {\n      throw new SftpException( \"Failed to rename from \" + oldPath + \" to \" + newPath, e );\n    }\n  }\n\n  @Override\n  public void close() {\n    try {\n      client.close();\n    } catch ( IOException ignored ) {\n      // Ignore","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSftpSession.java#L115-L151","documentation":"MinaSftpSession.delete() wraps IOException from the rmdir/remove path into an SftpException. Deletion failed server-side: wrong path, non-empty directory, or insufficient permission. Note the embedded isDirectory(path) call can itself throw for missing paths before delete even runs.","triggerScenarios":"Calling delete(path) when the path does not exist (stat inside delete fails), deleting a non-empty directory with rmdir, lacking delete permission, or the SFTP channel erroring mid-call.","commonSituations":"Cleanup jobs racing with other processes that already removed the file, trying to remove directories that still contain entries, service accounts without delete rights, or double-deletion in retry logic.","solutions":["Check file existence before deleting and treat already-gone as success (idempotent cleanup)","Empty the directory (recursively delete children) before calling delete on it","Verify the SSH user has delete permission on the target and its parent","Inspect e.getCause() for the SSH status code to choose the right remediation"],"exampleFix":"// before\nsession.delete(path);\n// after\ntry {\n  session.delete(path);\n} catch (SftpException e) {\n  if (!session.fileExists(path)) { log.info(\"Already deleted: \" + path); return; }\n  throw e;\n}","handlingStrategy":"validation","validationCode":"// idempotent delete: pre-check existence\nboolean exists;\ntry { session.size(path); exists = true; } catch (SftpException e) { exists = false; }\nif (exists) session.delete(path); // only delete when present","typeGuard":"boolean safeDelete(MinaSftpSession s, String p) {\n  try { if (s.fileExists(p)) { s.delete(p); } return true; }\n  catch (SftpException e) { return false; }\n}","tryCatchPattern":"try {\n  session.delete(path);\n} catch (SftpException e) {\n  Throwable c = e.getCause();\n  if (c != null && String.valueOf(c.getMessage()).toLowerCase().contains(\"no such file\")) return; // already gone\n  if (session.isDirectory(path)) throw new IOException(\"directory not empty; empty it first: \" + path, e);\n  throw e;\n}","preventionTips":["Make delete idempotent — missing target should be success in cleanup jobs","Empty directories before rmdir (SFTP rmdir fails on non-empty dirs)","Verify delete permission on the target and its parent for the SSH user","Serialize cleanup with other processes to avoid double-deletion races"],"tags":["sftp","ssh","delete","permission-denied"],"backgroundTag":"permission-denied","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"}