apache/dolphinscheduler · error · IllegalArgumentException

failed to download to ${fileDownloadPathNormalized}

Error message

failed to download to ${fileDownloadPathNormalized}

What it means

CosStorageOperator.download resolves the destination path under FileUtils.DATA_BASEDIR (the DS temp folder) and refuses to write anywhere else. If the normalized destination escapes that folder, it throws IllegalArgumentException to block path traversal outside the sandbox.

Source

Thrown at dolphinscheduler-storage-plugin/dolphinscheduler-storage-cos/src/main/java/org/apache/dolphinscheduler/plugin/storage/cos/CosStorageOperator.java:145

            throw new FileAlreadyExistsException("directory: " + cosKey + " already exists");
        }
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(0L);
        InputStream emptyContent = new ByteArrayInputStream(new byte[0]);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, cosKey, emptyContent, metadata);
        cosClient.putObject(putObjectRequest);
    }

    @SneakyThrows
    @Override
    public void download(String srcFilePath, String dstFilePath, boolean overwrite) {
        String cosKey = transformAbsolutePathToCOSKey(srcFilePath);
        Path dsTempFolder = Paths.get(FileUtils.DATA_BASEDIR).normalize().toAbsolutePath();
        Path fileDownloadPathNormalized = dsTempFolder.resolve(dstFilePath).normalize().toAbsolutePath();
        if (!fileDownloadPathNormalized.startsWith(dsTempFolder)) {
            // if the destination file path is NOT in DS temp folder (e.g., '/tmp/dolphinscheduler'),
            // an IllegalArgumentException should be thrown.
            throw new IllegalArgumentException("failed to download to " + fileDownloadPathNormalized);
        }
        File dstFile = fileDownloadPathNormalized.toFile();
        if (dstFile.isDirectory()) {
            Files.delete(dstFile.toPath());
        } else {
            FileUtils.createDirectoryWithPermission(dstFile.getParentFile().toPath(), FileUtils.PERMISSION_755);
        }

        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, cosKey);
        Download download = cosTransferManager.download(getObjectRequest, dstFile);
        download.waitForCompletion();
    }

    @Override
    public boolean exists(String fileName) {
        String cosKey = transformAbsolutePathToCOSKey(fileName);
        return cosClient.doesObjectExist(bucketName, cosKey);
    }

View on GitHub (pinned to 02eac45a1b)

Solutions

  1. Pass a path RELATIVE to the DS temp folder as dstFilePath
  2. Strip/normalize '..' segments and any leading '/' from user-supplied destination paths before calling download
  3. If the file truly must go elsewhere, download into the temp folder then move it with your own code

Example fix

// before
operator.download(srcPath, "/data/out/result.csv");
// after
operator.download(srcPath, "resources/out/result.csv"); // stays inside DATA_BASEDIR
Defensive patterns

Strategy: validation

Validate before calling

Path base = Paths.get(FileUtils.DATA_BASEDIR).normalize().toAbsolutePath();
Path dst = base.resolve(userDst).normalize().toAbsolutePath();
if (!dst.startsWith(base)) throw new IllegalArgumentException("dst must be inside DS temp folder");

Try / catch

try {
    operator.download(src, dst);
} catch (IllegalArgumentException e) {
    log.error("Destination escaped DS temp folder: {}", dst);
}

Prevention

When it happens

Trigger: Calling download(srcFilePath, dstFilePath) where dstFilePath resolves (after normalize().toAbsolutePath()) outside the dolphinscheduler temp base dir — e.g., an absolute dstFilePath like '/etc/foo' or a path containing enough '../' segments to escape.

Common situations: Passing an absolute destination path instead of a relative one; user-supplied resource paths containing '..'; misconfigured task output paths pointing to other system directories.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of apache/dolphinscheduler@02eac45a1b (2026-09-06). Data as JSON: /api/errors/1221730a2e9e5fea. Report an issue: GitHub.