apache/pulsar · error · IOException

Invalid path: ${path}

Error message

Invalid path: ${path}

What it means

FileSystemPackagesStorage.getPath resolves a user-supplied path relative to the configured storage directory. After normalization it verifies the resulting absolute path still starts with the storage root; if not, the path escapes the storage directory (path traversal, e.g. via "../") and an IOException is thrown. This guards against reading/writing/deleting files outside the package storage.

Source

Thrown at pulsar-package-management/filesystem-storage/src/main/java/org/apache/pulsar/packages/management/storage/filesystem/FileSystemPackagesStorage.java:67

    private final File storagePath;

    FileSystemPackagesStorage(PackagesStorageConfiguration configuration) {
        String storagePath = configuration.getProperty(STORAGE_PATH);
        if (storagePath != null) {
            this.storagePath = new File(storagePath);
        } else {
            this.storagePath = new File(DEFAULT_STORAGE_PATH);
        }
    }

    private File getPath(String path) throws IOException {
        // Normalize the path to remove any redundant path elements
        File f = Paths.get(storagePath.toString(), path).normalize().toFile();

        // Ensure the normalized path is still within the storagePath
        if (!f.getAbsolutePath().startsWith(storagePath.getAbsolutePath())) {
            throw new IOException("Invalid path: " + path);
        }

        if (!f.getParentFile().exists()) {
            if (!f.getParentFile().mkdirs()) {
                throw new RuntimeException("Failed to create parent dirs for " + path);
            }
        }
        return f;
    }

    @Override
    public void initialize() {
        if (!storagePath.exists()) {
            if (!storagePath.mkdirs()) {
                throw new RuntimeException("Failed to create base storage directory at " + storagePath);
            }
        }

View on GitHub (pinned to 820761864e)

Solutions

  1. Remove ".." segments and use paths relative to the storage root.
  2. Check the storagePath configuration (packagesManagementStorageProvider config) — set it to a canonical absolute directory and ensure paths resolve under it.
  3. Resolve symlinks: the check compares string prefixes of absolute paths, so a symlinked storagePath can false-positive; use the real canonical path in configuration.
  4. Inspect the failing path value in the message for traversal sequences or encoding issues.

Example fix

// before
storage.read("../../etc/passwd"); // throws Invalid path
// after
storage.read("tenant/namespace/package/metadata"); // stays under storage root
Defensive patterns

Strategy: validation

Validate before calling

static boolean isPathInsideStorage(String path, String storageRoot) throws java.io.IOException {
    java.nio.file.Path resolved = java.nio.file.Paths.get(storageRoot, path).normalize().toAbsolutePath().toRealPath();
    java.nio.file.Path root = java.nio.file.Paths.get(storageRoot).toAbsolutePath().toRealPath();
    return resolved.startsWith(root);
}

Try / catch

try {
    storage.readAsync(path).get();
} catch (java.io.IOException e) {
    if (e.getMessage().startsWith("Invalid path:")) {
        log.warn("Rejected path outside storage root: {}", path);
        throw new RestException(Response.Status.BAD_REQUEST, "invalid path");
    }
    throw e;
}

Prevention

When it happens

Trigger: read/delete/list calls with paths containing ".." segments or absolute components that resolve outside storagePath; storagePath itself configured as a relative path or symlink so the startsWith prefix check fails even for legitimate paths.

Common situations: Path traversal attempt in a request; misconfigured storagePath (relative path, trailing differences, symlinked directory) making valid paths appear outside the root; storage backends migrated between machines with different layouts.

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/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/c108edf585e3281e. Report an issue: GitHub.