{"record":{"id":"dbf9cd002bc50356","repo":"YunaiV/yudao-cloud","slug":"error-dbf9cd","errorCode":null,"errorMessage":"上传文件到目标目录 ({}) 失败","messagePattern":"上传文件到目标目录 \\((.+?)\\) 失败","errorType":"exception","errorClass":"JschRuntimeException","httpStatus":500,"severity":"error","filePath":"yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java","lineNumber":63,"sourceCode":"        FtpConfig ftpConfig = new FtpConfig(config.getHost(), config.getPort(), config.getUsername(), config.getPassword(),\n                CharsetUtil.CHARSET_UTF_8, null, null);\n        ftpConfig.setConnectionTimeout(CONNECTION_TIMEOUT);\n        ftpConfig.setSoTimeout(SO_TIMEOUT);\n        this.sftp = new Sftp(ftpConfig);\n    }\n\n    @Override\n    public String upload(byte[] content, String path, String type) {\n        // 执行写入\n        String filePath = getFilePath(path);\n        String fileName = FileUtil.getName(filePath);\n        String dir = StrUtil.removeSuffix(filePath, fileName);\n        File file = FileUtils.createTempFile(content);\n        reconnectIfTimeout();\n        sftp.mkDirs(dir); // 需要创建父目录，不然会报错\n        boolean success = sftp.upload(filePath, file);\n        if (!success) {\n            throw new JschRuntimeException(StrUtil.format(\"上传文件到目标目录 ({}) 失败\", filePath));\n        }\n        // 拼接返回路径\n        return super.formatFileUrl(config.getDomain(), path);\n    }\n\n    @Override\n    public void delete(String path) {\n        String filePath = getFilePath(path);\n        reconnectIfTimeout();\n        sftp.delFile(filePath);\n    }\n\n    @Override\n    public byte[] getContent(String path) {\n        String filePath = getFilePath(path);\n        File destFile = FileUtils.createTempFile();\n        reconnectIfTimeout();\n        sftp.download(filePath, destFile);","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/YunaiV/yudao-cloud/blob/477be9dd49ab7223a972a6abdff0684d6423dec3/yudao-module-infra/yudao-module-infra-server/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/sftp/SftpFileClient.java#L45-L81","documentation":"SftpFileClient.upload throws hutool JschRuntimeException after sftp.mkDirs(dir) plus sftp.upload(filePath, tempFile) return false. The client already writes the payload to a local temp file and creates parent dirs, so failure means the SSH/SFTP channel rejected the write: no permission on the remote dir, wrong basePath, disk/quota exceeded, or a channel that went stale despite reconnectIfTimeout().","triggerScenarios":"Uploading through an SFTP file config whose user lacks write permission on basePath; remote directory quota full; basePath pointing to a read-only mount; key-based auth partially working (login ok, write denied by chroot/AllowChown); stale channel after long idle.","commonSituations":"SFTP chroot jail not permitting mkdir/write; wrong home-relative basePath after server reconfiguration; disk full on the SFTP host; permission bits 0555 on the target dir.","solutions":["SSH in as the configured user and verify write+mkdir on basePath: sftp> cd /upload; mkdir test; put file","Fix remote permissions (chmod/chown) or adjust basePath to a writable directory","Check remote disk space and quotas (df -h)","Re-validate the whole config (host, port, username, auth, basePath) from the infra file config page"],"exampleFix":"# before: basePath not writable by sftp user\nbasePath: /srv/sftp/data   # owned by root, 755\n\n# after\nsudo chown sftpuser:sftpuser /srv/sftp/data && sudo chmod 755 /srv/sftp/data\n# or set basePath to a dir the user owns, then retry upload","handlingStrategy":"retry","validationCode":"// pre-flight: verify sftp write access\ntry (Sftp probe = new Sftp(host, port, user, pass)) {\n    probe.mkDirs(basePath);\n    probe.upload(basePath + \"/.probe\", new ByteArrayInputStream(new byte[1]));\n    probe.delFile(basePath + \"/.probe\");\n} catch (Exception e) {\n    throw new IllegalStateException(\"SFTP 目录不可写: \" + basePath, e);\n}","typeGuard":null,"tryCatchPattern":"try {\n    return sftpFileClient.upload(content, path, type);\n} catch (JschRuntimeException e) {\n    log.warn(\"SFTP upload failed for {}, retrying once\", path, e);\n    return sftpFileClient.upload(content, path, type); // stale-channel failures often clear on reconnect\n}","preventionTips":["Grant the SFTP user write+mkdir on basePath (chroot jail must allow it)","Run a test upload when saving the file config in the admin UI","Watch remote disk/quota; alert before it fills","Retry once on JschRuntimeException — dropped channels are common over long idle"],"tags":["infra","file","sftp","upload","permissions","ssh"],"backgroundTag":null,"analyzedSha":"477be9dd49ab7223a972a6abdff0684d6423dec3","analyzedAt":"2026-08-14T13:35:31.121Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}