appsmithorg/appsmith · error · AppsmithPluginException

PE-DSE-5003

PE-DSE-5003

Error message

The provided SSH key could not be parsed.

What it means

Thrown by SSHUtils.createSSHTunnel while reading the uploaded private key bytes into a string. The first try-with-resources reads key.getDecodedContent() line by line; any IOException (e.g. malformed byte sequence under UTF-8, truncated content) is wrapped as PLUGIN_DATASOURCE_ARGUMENT_ERROR with the SSH_KEY_PARSING_ERROR_MSG prefix and the underlying exception chained.

Source

Thrown at app/server/appsmith-interfaces/src/main/java/com/appsmith/external/helpers/SSHUtils.java:91

         * folder. However, in our case because we cannot allow users to add trusted public keys in known_hosts
         * folder for cloud hosted instances I am turning this check off.
         */
        client.addHostKeyVerifier(new PromiscuousVerifier());

        client.connect(sshHost, sshPort);
        Reader targetReader = new InputStreamReader(new ByteArrayInputStream(key.getDecodedContent()));
        String keyContent;
        KeyProvider keyFile = null;
        try (Reader reader = new StringReader(new String(key.getDecodedContent(), StandardCharsets.UTF_8));
                BufferedReader bufferedReader = new BufferedReader(reader)) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line).append("\n");
            }
            keyContent = sb.toString();
        } catch (IOException e) {
            throw new AppsmithPluginException(
                    AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR,
                    SSH_KEY_PARSING_ERROR_MSG + e.getMessage(),
                    e);
        }
        try {
            if (keyContent.contains(OPENSSH_PEM_HEADER)) {
                // Use BouncyCastle to handle OpenSSH keys
                if (Security.getProvider("BC") == null) {
                    Security.addProvider(new BouncyCastleProvider());
                }
                OpenSSHKeyFile openSSHKeyFile = new OpenSSHKeyFile();
                openSSHKeyFile.init(new StringReader(keyContent));
                keyFile = openSSHKeyFile;
            } else if (keyContent.contains(PKCS_8_PEM_HEADER) || keyContent.contains(PKCS_1_PEM_HEADER)) {
                // Handle PEM (PKCS#8) and RSA PEM formats
                PKCS8KeyFile pkcs8KeyFile = new PKCS8KeyFile();
                pkcs8KeyFile.init(new StringReader(keyContent));
                keyFile = pkcs8KeyFile;

View on GitHub (pinned to 8cd9021c24)

Solutions

  1. Re-export the private key in OpenSSH or PEM format: ssh-keygen -p -f id_rsa -m PEM, then re-upload.
  2. Open the key file in a text editor and confirm it begins with -----BEGIN OPENSSH PRIVATE KEY----- or -----BEGIN RSA PRIVATE KEY----- / -----BEGIN PRIVATE KEY-----.
  3. Ensure no trailing whitespace or BOM is introduced when copy/pasting; upload the raw file rather than pasting its contents.
  4. If the key was generated by a cloud portal (AWS, GCP), download the provided .pem directly without re-encoding.

Example fix

// before
try (Reader reader = new StringReader(new String(key.getDecodedContent(), StandardCharsets.UTF_8))) { ... }

// after - tolerate malformed bytes by reading with a replace decoder and validating structure
String keyContent = new String(key.getDecodedContent(), StandardCharsets.UTF_8);
if (!keyContent.contains("PRIVATE KEY")) {
    throw new AppsmithPluginException(
        AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR,
        SSH_KEY_PARSING_ERROR_MSG + "content does not look like a PEM/OpenSSH private key");
}
Defensive patterns

Strategy: validation

Validate before calling

byte[] content = key.getDecodedContent();
if (content == null || content.length == 0) {
    throw new IllegalArgumentException("SSH key content is empty");
}
String preview = new String(content, StandardCharsets.UTF_8);
if (!preview.contains("PRIVATE KEY")) {
    throw new IllegalArgumentException("Uploaded file does not look like a private key");
}
// safe to proceed to SSHUtils.createSSHTunnel

Type guard

public static boolean looksLikePrivateKey(byte[] content) {
    if (content == null || content.length == 0) return false;
    String s = new String(content, StandardCharsets.UTF_8);
    return s.contains("PRIVATE KEY");
}

Try / catch

try {
    SSHTunnelContext ctx = SSHUtils.createSSHTunnel(host, port, user, key, dbHost, dbPort);
} catch (AppsmithPluginException e) {
    if (e.getMessage() != null && e.getMessage().startsWith(SSH_KEY_PARSING_ERROR_MSG)) {
        // surface a user-actionable message about key format
        throw new IllegalArgumentException("SSH key could not be read; re-export in OpenSSH/PEM format", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Uploading an SSH private key whose bytes are not valid UTF-8 (binary corruption, double-encoded base64), an empty key file, or a key whose content was truncated during upload. The exception is raised before any format detection occurs - it is purely about reading the bytes.

Common situations: Pasting a key with stray terminal control characters; uploading a .ppk (PuTTY) key which is not raw PEM; a copy/paste that lost the BEGIN/END lines; encoding mismatches when the key was stored as base64-of-base64.

Related errors


AI-assisted analysis of appsmithorg/appsmith@8cd9021c24 (2026-08-12). Data as JSON: /api/errors/d2257430a086d6d9. Report an issue: GitHub.