apache/kafka · error · ConfigException

Path normalisedPath could not be resolved

Error message

Path normalisedPath could not be resolved

What it means

Thrown by AllowedPaths.getAllowedPaths() when Path.toRealPath() raises an IOException during resolution of an otherwise valid, existing absolute path. This is the fallback ConfigException (with the underlying IOException as cause) for filesystem-level resolution failures that the existence check did not catch, such as broken symlinks encountered mid-resolution or permission errors reading directory metadata.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/config/internals/AllowedPaths.java:56

        this.allowedPaths = getAllowedPaths(configValue);
    }

    private List<Path> getAllowedPaths(String configValue) {
        if (configValue != null && !configValue.isEmpty()) {
            List<Path> allowedPaths = new ArrayList<>();

            Arrays.stream(configValue.split(",")).forEach(b -> {
                Path normalisedPath = Paths.get(b).normalize();

                if (!normalisedPath.isAbsolute()) {
                    throw new ConfigException("Path " + normalisedPath + " is not absolute");
                } else if (!Files.exists(normalisedPath)) {
                    throw new ConfigException("Path " + normalisedPath + " does not exist");
                } else {
                    try {
                        allowedPaths.add(normalisedPath.toRealPath());
                    } catch (IOException e) {
                        throw new ConfigException("Path " + normalisedPath + " could not be resolved", e);
                    }
                }
            });

            return allowedPaths;
        }

        return null;
    }

    /**
     * Checks if the given {@code path} resides in the configured {@code allowed.paths}.
     * If {@code allowed.paths} is not configured, the given Path is returned as allowed.
     * @param path the Path to check if allowed
     * @return Path that can be accessed or null if the given Path does not reside in the configured {@code allowed.paths}.
     */
    public Path parseUntrustedPath(String path) {
        Path parsedPath = Paths.get(path);

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Inspect the underlying IOException cause in the log (ConfigException wraps it) to see the exact I/O failure.
  2. Resolve or remove broken symlinks along the path: readlink -f <path> && ls -la.
  3. Grant the JVM user read+execute permissions on every directory in the chain.
  4. Replace the symlinked entry with the canonical real target path in allowed.paths.

Example fix

# before
ln -s /mnt/missing /etc/kafka/secrets
allowed.paths=/etc/kafka/secrets

# after
# fix or recreate the symlink target
ln -sfn /mnt/present/secrets /etc/kafka/secrets
allowed.paths=/mnt/present/secrets
Defensive patterns

Strategy: try-catch

Validate before calling

// Best-effort probe of toRealPath() before AllowedPaths construction:
for (String raw : configValue.split(",")) {
    Path p = Paths.get(raw.trim()).normalize();
    try {
        p.toRealPath(); // surfaces broken symlinks / permission issues early
    } catch (IOException e) {
        throw new IllegalArgumentException(
            "allowed.paths entry '" + p + "' cannot be resolved: " + e.getMessage(), e);
    }
}

Try / catch

try {
    AllowedPaths ap = new AllowedPaths(configValue);
} catch (ConfigException e) {
    if (e.getMessage().endsWith("could not be resolved")
            && e.getCause() instanceof IOException) {
        // broken symlink / I/O fault: log cause, retry, or fall back to a known-good path
    } else { throw e; }
}

Prevention

When it happens

Trigger: AllowedPaths construction with an allowed.paths entry that is absolute and exists at the top level but contains a broken symlink, a cyclic symlink chain, or an intermediate directory the JVM cannot read/stat. toRealPath() throws IOException, wrapped as ConfigException.

Common situations: allowed.paths entry points at a symlink whose target is missing or unreachable. Container layers where intermediate mount points are not stat-able by the JVM user. NFS/network filesystem hiccups during startup. SELinux/AppArmor denying stat on an intermediate directory.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/eecc49a2367e6747.json. Report an issue: GitHub.