kestra-io/kestra · error · IllegalArgumentException

Path must not contain '../'

Error message

Path must not contain '../'

What it means

Thrown by 'fileURI' inside getNamespaceFileURI() when the URI path string contains '../'. This is an explicit path-traversal guard: namespace file access is restricted to within the namespace, so escaping via parent-directory segments is rejected before any storage operation.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/FileURIFunction.java:57

        defaults.put(NAMESPACE, null);
        defaults.put(REVISION, null);
        return defaults;
    }

    @Override
    protected Object fileFunction(EvaluationContext context, URI path, String namespace, String tenantId, Map<String, Object> args) throws IOException {
        return switch (path.getScheme()) {
            case StorageContext.KESTRA_SCHEME -> path.toString();
            case LocalPath.FILE_SCHEME -> path.toString();
            case Namespace.NAMESPACE_FILE_SCHEME -> getNamespaceFileURI(path, namespace, tenantId, args);
            default -> throw new IllegalArgumentException(SCHEME_NOT_SUPPORTED_ERROR.formatted(path));
        };
    }

    private String getNamespaceFileURI(URI path, String namespace, String tenantId, Map<String, Object> args) throws IOException {
        String pathStr = path.getPath();
        if (pathStr.contains("../")) {
            throw new IllegalArgumentException("Path must not contain '../'");
        }
        Namespace namespaceStorage = namespaceFactory.get().of(tenantId, namespace, storageInterface.get());
        Path filePath = NamespaceFile.normalize(Path.of(pathStr));

        if (args.containsKey(REVISION)) {
            Integer revision;
            try {
                revision = Integer.parseInt(args.get(REVISION).toString());
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("The 'fileURI' function expects the 'revision' argument to be a valid integer.");
            }
            try {
                namespaceStorage.getFileContent(filePath, revision).close();
            } catch (FileNotFoundException e) {
                throw new FileNotFoundException("Revision " + revision + " of file '" + filePath + "' was not found in namespace '" + namespace + "'.");
            }
            NamespaceFile namespaceFile = NamespaceFile.of(namespace, filePath, revision);
            return namespaceFile.uri().toString();

View on GitHub (pinned to 823fada927)

Solutions

  1. Remove any '../' from the path; reference files only within the target namespace.
  2. If the path is built from user input, sanitize/normalize it (resolve and confirm it stays under the namespace root) before passing it to fileURI().
  3. Move the needed file into the correct namespace instead of traversing out.

Example fix

# before
uri: "{{ fileURI('namespace:///../shared/secret.yaml') }}"
# after - reference the file within the correct namespace
uri: "{{ fileURI('namespace:///shared/secret.yaml') }}"
Defensive patterns

Strategy: validation

Validate before calling

# Reject '../' in the path before calling fileURI().
# {{ (not (path contains '../')) ? fileURI(path) : null }}

Prevention

When it happens

Trigger: Calling fileURI() with a namespace file path that includes '../' to climb above the namespace root, e.g. namespace:///../secret or a path built from user input that injects '../'.

Common situations: Constructing a namespace file path dynamically from untrusted user input (flow inputs) without sanitizing; attempting to read a file outside the current namespace; copy-pasting a filesystem-relative path into a namespace URI.

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/8593bbd9bfa302d4. Report an issue: GitHub.