pentaho/pentaho-kettle · error · SftpException
Failed to create directory:
Error message
Failed to create directory:
What it means
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.
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
Example fix
// before
session.mkdir("/data/2026/09/uploads");
// after
String[] parts = "/data/2026/09/uploads".split("/");
String cur = "";
for (String p : parts) {
if (p.isEmpty()) { cur = "/"; continue; }
cur = cur.endsWith("/") ? cur + p : cur + "/" + p;
try { session.mkdir(cur); } catch (SftpException e) { /* already exists - continue */ }
} Defensive patterns
Strategy: validation
Validate before calling
// recursive pre-creation so parent is never missing
String cur = "";
for (String part : path.split("/")) {
if (part.isEmpty()) { cur = "/"; continue; }
cur = (cur.equals("/") ? "" : cur) + "/" + part;
try { session.mkdir(cur); } catch (SftpException alreadyExists) { /* continue */ }
} Type guard
boolean canMkdir(MinaSftpSession s, String parent) {
try { s.mkdir(parent + "/.probe"); s.delete(parent + "/.probe"); return true; }
catch (SftpException e) { return false; }
} Try / catch
try {
session.mkdir(path);
} catch (SftpException e) {
if (session.fileExists(path)) { /* treat as success */ return; }
throw new IOException("mkdir failed for " + path + ": " + e.getCause(), e);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
- Failed to delete:
- Failed to upload file:
- Failed to check if path is directory:
- Failed to download file:
- Failed to get file size:
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/b3c139d7ac061a22.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSftpSession.java:120
while ( ( r = source.read( buf ) ) >= 0 ) {
if ( r == 0 ) {
continue;
}
client.write( h, off, buf, 0, r );
off += r;
}
}
} catch ( IOException e ) {
throw new SftpException( "Failed to upload file: " + remote, e );
}
}
@Override
public void mkdir( String path ) throws SftpException {
try {
client.mkdir( path );
} catch ( IOException e ) {
throw new SftpException( "Failed to create directory: " + path, e );
}
}
@Override
public void delete( String path ) throws SftpException {
try {
if ( isDirectory( path ) ) {
client.rmdir( path );
} else {
client.remove( path );
}
} catch ( IOException e ) {
throw new SftpException( "Failed to delete: " + path, e );
}
}
@Override
public void rename( String oldPath, String newPath ) throws SftpException {View on GitHub (pinned to f3058517a1)