kestra-io/kestra · error · PebbleException

Unable to read the file {path}

Error message

Unable to read the file {path}

What it means

AbstractFileFunction.execute() branches on the runtime type of the 'path' argument: URI, String, or else. The else branch throws 'Unable to read the file ' + path when path is neither a URI nor a String (e.g. a number, list, map, or boolean).

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/AbstractFileFunction.java:98

                if (str.startsWith(KESTRA_SCHEME)) {
                    fileUri = URI.create(str);
                    namespace = checkAllowedFileAndReturnNamespace(context, fileUri);
                } else if (str.startsWith(LocalPath.FILE_PROTOCOL)) {
                    fileUri = URI.create(str);
                    namespace = checkEnabledLocalFileAndReturnNamespace(args, flow);
                } else if (str.startsWith(Namespace.NAMESPACE_FILE_SCHEME)) {
                    fileUri = URI.create(str);
                    namespace = checkedAllowedNamespaceAndReturnNamespace(args, fileUri, tenantId, flow);
                } else if (URI_PATTERN.matcher(str).matches()) {
                    // it is an unsupported URI
                    throw new IllegalArgumentException(SCHEME_NOT_SUPPORTED_ERROR.formatted(str));
                } else {
                    fileUri = URI.create(Namespace.NAMESPACE_FILE_SCHEME + ":///" + str);
                    namespace = (String) Optional.ofNullable(args.get(NAMESPACE)).orElse(flow.get(NAMESPACE));
                    namespaceService.get().checkAllowedNamespace(tenantId, namespace, tenantId, flow.get(NAMESPACE));
                }
            } else {
                throw new PebbleException(null, "Unable to read the file " + path, lineNumber, self.getName());
            }
            return fileFunction(context, fileUri, namespace, tenantId, args);
        } catch (IOException | IllegalArgumentException e) { // IllegalArgumentException may be thrown for path traversal, catch it to have proper error handling
            throw new PebbleException(e, e.getMessage(), lineNumber, self.getName());
        }
    }

    @Override
    public List<String> getArgumentNames() {
        return List.of(PATH, NAMESPACE);
    }

    @Override
    public Map<String, String> getArgumentDefaults() {
        return Map.of(
            PATH, "outputs.download.uri",
            NAMESPACE, "flow.namespace"
        );

View on GitHub (pinned to 823fada927)

Solutions

  1. Ensure the path argument resolves to a String or a URI at runtime (inspect the upstream output schema).
  2. Coerce explicitly when safe: {{ read(myVar ~ '') }} to force a string, or extract the URI field with a known path.
  3. Avoid passing collections/scalars; read() only accepts a single file reference.

Example fix

// before
{{ read(outputs.task.id) }}
// after
{{ read(outputs.task.outputFiles['data']) }}
Defensive patterns

Strategy: type-guard

Validate before calling

// Pebble: ensure path is a string URI before calling read
{% if outputs.x.uri is string %}{{ read(outputs.x.uri) }}{% endif %}

Type guard

// Java: only accept String or URI
if (!(path instanceof String || path instanceof URI)) {
    throw new IllegalArgumentException("path must be a String or URI, got " + path.getClass());
}

Prevention

When it happens

Trigger: Calling read() with a numeric id, a list, a map, or any non-String/non-URI value: {{ read(123) }} or {{ read(outputs.task.rows) }} where rows is a list.

Common situations: Indexing into a list/map and passing the element assuming it is a URI string; binding path to an output field whose type is a structured object; leftover debug value.

Related errors


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