{"record":{"id":"b3c139d7ac061a22","repo":"pentaho/pentaho-kettle","slug":"failed-to-create-directory","errorCode":null,"errorMessage":"Failed to create directory: ","messagePattern":"Failed to create directory: ","errorType":"exception","errorClass":"SftpException","httpStatus":null,"severity":"error","filePath":"engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSftpSession.java","lineNumber":120,"sourceCode":"        while ( ( r = source.read( buf ) ) >= 0 ) {\n          if ( r == 0 ) {\n            continue;\n          }\n          client.write( h, off, buf, 0, r );\n          off += r;\n        }\n      }\n    } catch ( IOException e ) {\n      throw new SftpException( \"Failed to upload file: \" + remote, e );\n    }\n  }\n\n  @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 {","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSftpSession.java#L102-L138","documentation":"MinaSftpSession.mkdir() wraps IOException from client.mkdir(path) into an SftpException. The remote directory could not be created, usually because the parent directory is missing or the user lacks write permission. The server-side SSH_FXP_STATUS code is in the cause.","triggerScenarios":"Calling mkdir(path) when the parent directory does not exist (SFTP mkdir is not recursive), the SSH user lacks write permission on the parent, or the directory already exists and the server rejects it.","commonSituations":"Creating deep paths like /data/2026/09/uploads in one call when /data/2026 does not exist, deploying pipelines that assume a service account has write access it does not, or a chrooted user trying paths outside their root.","solutions":["Create parent directories one level at a time (or add a recursive mkdir helper) before creating the leaf","Check write permission for the SSH user on the parent directory","Ignore or pre-check the case where the directory already exists","Read e.getCause() to distinguish permission-denied from no-such-file"],"exampleFix":"// before\nsession.mkdir(\"/data/2026/09/uploads\");\n// after\nString[] parts = \"/data/2026/09/uploads\".split(\"/\");\nString cur = \"\";\nfor (String p : parts) {\n  if (p.isEmpty()) { cur = \"/\"; continue; }\n  cur = cur.endsWith(\"/\") ? cur + p : cur + \"/\" + p;\n  try { session.mkdir(cur); } catch (SftpException e) { /* already exists - continue */ }\n}","handlingStrategy":"validation","validationCode":"// recursive pre-creation so parent is never missing\nString cur = \"\";\nfor (String part : path.split(\"/\")) {\n  if (part.isEmpty()) { cur = \"/\"; continue; }\n  cur = (cur.equals(\"/\") ? \"\" : cur) + \"/\" + part;\n  try { session.mkdir(cur); } catch (SftpException alreadyExists) { /* continue */ }\n}","typeGuard":"boolean canMkdir(MinaSftpSession s, String parent) {\n  try { s.mkdir(parent + \"/.probe\"); s.delete(parent + \"/.probe\"); return true; }\n  catch (SftpException e) { return false; }\n}","tryCatchPattern":"try {\n  session.mkdir(path);\n} catch (SftpException e) {\n  if (session.fileExists(path)) { /* treat as success */ return; }\n  throw new IOException(\"mkdir failed for \" + path + \": \" + e.getCause(), e);\n}","preventionTips":["SFTP mkdir is not recursive — create each path level in order","Treat 'already exists' on mkdir as success for idempotent pipelines","Verify the SSH user's write permissions (and chroot root) on the parent","Prefer creating parent dirs during provisioning, not at pipeline runtime"],"tags":["sftp","ssh","mkdir","permission-denied"],"backgroundTag":"mkdir-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"}