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
- Ensure the path argument resolves to a String or a URI at runtime (inspect the upstream output schema).
- Coerce explicitly when safe: {{ read(myVar ~ '') }} to force a string, or extract the URI field with a known path.
- 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
- Confirm the path argument resolves to a String or URI at runtime.
- Do not pass lists, maps, numbers, or booleans to read().
- Inspect the upstream output schema before binding path.
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
- getErrorMessage() [abstract: subclass-defined ERROR_MESSAGE]
- Cannot process the URI %s: scheme not supported.
- IOException|IllegalArgumentException: e.getMessage()
- Unable to read the file '{path}' as it is not an execution f
- Cannot concat {} with {}
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/07eb71e59572fd87.
Report an issue: GitHub.