{"record":{"id":"d2257430a086d6d9","repo":"appsmithorg/appsmith","slug":"pe-dse-5003","errorCode":"PE-DSE-5003","errorMessage":"The provided SSH key could not be parsed.","messagePattern":"The provided SSH key could not be parsed\\.","errorType":"error_code","errorClass":"AppsmithPluginException","httpStatus":500,"severity":"error","filePath":"app/server/appsmith-interfaces/src/main/java/com/appsmith/external/helpers/SSHUtils.java","lineNumber":91,"sourceCode":"         * folder. However, in our case because we cannot allow users to add trusted public keys in known_hosts\n         * folder for cloud hosted instances I am turning this check off.\n         */\n        client.addHostKeyVerifier(new PromiscuousVerifier());\n\n        client.connect(sshHost, sshPort);\n        Reader targetReader = new InputStreamReader(new ByteArrayInputStream(key.getDecodedContent()));\n        String keyContent;\n        KeyProvider keyFile = null;\n        try (Reader reader = new StringReader(new String(key.getDecodedContent(), StandardCharsets.UTF_8));\n                BufferedReader bufferedReader = new BufferedReader(reader)) {\n            StringBuilder sb = new StringBuilder();\n            String line;\n            while ((line = bufferedReader.readLine()) != null) {\n                sb.append(line).append(\"\\n\");\n            }\n            keyContent = sb.toString();\n        } catch (IOException e) {\n            throw new AppsmithPluginException(\n                    AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR,\n                    SSH_KEY_PARSING_ERROR_MSG + e.getMessage(),\n                    e);\n        }\n        try {\n            if (keyContent.contains(OPENSSH_PEM_HEADER)) {\n                // Use BouncyCastle to handle OpenSSH keys\n                if (Security.getProvider(\"BC\") == null) {\n                    Security.addProvider(new BouncyCastleProvider());\n                }\n                OpenSSHKeyFile openSSHKeyFile = new OpenSSHKeyFile();\n                openSSHKeyFile.init(new StringReader(keyContent));\n                keyFile = openSSHKeyFile;\n            } else if (keyContent.contains(PKCS_8_PEM_HEADER) || keyContent.contains(PKCS_1_PEM_HEADER)) {\n                // Handle PEM (PKCS#8) and RSA PEM formats\n                PKCS8KeyFile pkcs8KeyFile = new PKCS8KeyFile();\n                pkcs8KeyFile.init(new StringReader(keyContent));\n                keyFile = pkcs8KeyFile;","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/appsmithorg/appsmith/blob/8cd9021c24cdbea1c3c12c966073708e83db60c2/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/helpers/SSHUtils.java#L73-L109","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-export the private key in OpenSSH or PEM format: ssh-keygen -p -f id_rsa -m PEM, then re-upload.","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-----.","Ensure no trailing whitespace or BOM is introduced when copy/pasting; upload the raw file rather than pasting its contents.","If the key was generated by a cloud portal (AWS, GCP), download the provided .pem directly without re-encoding."],"exampleFix":"// before\ntry (Reader reader = new StringReader(new String(key.getDecodedContent(), StandardCharsets.UTF_8))) { ... }\n\n// after - tolerate malformed bytes by reading with a replace decoder and validating structure\nString keyContent = new String(key.getDecodedContent(), StandardCharsets.UTF_8);\nif (!keyContent.contains(\"PRIVATE KEY\")) {\n    throw new AppsmithPluginException(\n        AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR,\n        SSH_KEY_PARSING_ERROR_MSG + \"content does not look like a PEM/OpenSSH private key\");\n}","handlingStrategy":"validation","validationCode":"byte[] content = key.getDecodedContent();\nif (content == null || content.length == 0) {\n    throw new IllegalArgumentException(\"SSH key content is empty\");\n}\nString preview = new String(content, StandardCharsets.UTF_8);\nif (!preview.contains(\"PRIVATE KEY\")) {\n    throw new IllegalArgumentException(\"Uploaded file does not look like a private key\");\n}\n// safe to proceed to SSHUtils.createSSHTunnel","typeGuard":"public static boolean looksLikePrivateKey(byte[] content) {\n    if (content == null || content.length == 0) return false;\n    String s = new String(content, StandardCharsets.UTF_8);\n    return s.contains(\"PRIVATE KEY\");\n}","tryCatchPattern":"try {\n    SSHTunnelContext ctx = SSHUtils.createSSHTunnel(host, port, user, key, dbHost, dbPort);\n} catch (AppsmithPluginException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(SSH_KEY_PARSING_ERROR_MSG)) {\n        // surface a user-actionable message about key format\n        throw new IllegalArgumentException(\"SSH key could not be read; re-export in OpenSSH/PEM format\", e);\n    }\n    throw e;\n}","preventionTips":["Validate key content is non-empty and contains a PRIVATE KEY marker before upload.","Re-export keys via ssh-keygen -m PEM to guarantee UTF-8 PEM output.","Never paste keys through rich-text editors that may alter bytes."],"tags":["ssh","ssh-tunnel","key-parsing","io","datasource-config"],"backgroundTag":null,"analyzedSha":"8cd9021c24cdbea1c3c12c966073708e83db60c2","analyzedAt":"2026-08-12T22:14:19.293Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}