xpipe-io/xpipe · error · BeaconClientException
File path " + msg.getPath() + " is not absolute
Error message
File path " + msg.getPath() + " is not absolute
What it means
FsReadExchange requires an absolute file path on the target filesystem. The client sent a relative path, and the shell-based filesystem cannot resolve it unambiguously (the working directory of the remote shell is not part of the API contract). The daemon fails fast before touching the filesystem.
Solutions
- Normalize the path to absolute before calling: prefix with the known base directory
- Use Path.toAbsolutePath() (or equivalent) on the client side against the intended base dir
- For remote systems, anchor to a known root such as '/' or the user's home obtained from the shell
- Validate with path.isAbsolute() and reject or resolve before sending
Example fix
// before
client.readFile(store, Path.of("logs/app.log"));
// after
Path p = Path.of("logs/app.log");
if (!p.isAbsolute()) {
p = homeDir.resolve(p).normalize();
}
client.readFile(store, p); Defensive patterns
Strategy: validation
Validate before calling
if (path == null || !path.isAbsolute()) {
throw new IllegalArgumentException("Path must be absolute: " + path);
} Type guard
boolean isUsableRemotePath(Path p) {
return p != null && p.isAbsolute() && !p.normalize().toString().contains("..");
} Try / catch
try {
client.readFile(store, path);
} catch (BeaconClientException e) {
if (e.getMessage().contains("is not absolute")) {
path = baseDir.resolve(path).normalize();
client.readFile(store, path);
} else throw e;
} Prevention
- Always resolve user-supplied paths against an explicit base directory
- Remember remote resolution depends on the target OS (drive letters on Windows)
- Reject relative paths at your API boundary
When it happens
Trigger: Calling the fs read beacon endpoint with msg.getPath() set to a relative path like 'file.txt' or './sub/file.txt' instead of an absolute path such as '/etc/file.txt' or 'C:\\file.txt'.
Common situations: User-supplied paths passed straight through to the API; composing paths from a config value missing its leading separator; paths constructed on Windows-style vs POSIX-style systems and then used remotely; scripts running from a working directory and assuming relative resolution.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- File path " + msg.getPath() + " is not absolute
- Directory " + msg.getPath().getParent() + " does not exist
- File " + msg.getPath() + " does not exist
- Authentication failed
- Cannot delete category: " + cat.getName()
AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06).
Data as JSON: /api/errors/c072b9df166b496f.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/io/xpipe/app/beacon/api/FsReadExchange.java:37
import java.io.OutputStream;
import java.nio.file.Files;
import java.util.UUID;
public class FsReadExchange extends BeaconInterface<FsReadExchange.Request> {
@Override
public String getPath() {
return "/fs/read";
}
@Override
@SneakyThrows
public Object handle(HttpExchange exchange, Request msg) {
var shell = AppBeaconServer.get().getCache().getShellSession(msg.getStore());
var fs = new ShellFileSystem(shell.getControl());
if (!msg.getPath().isAbsolute()) {
throw new BeaconClientException("File path " + msg.getPath() + " is not absolute");
}
if (!fs.fileExists(msg.getPath())) {
throw new BeaconClientException("File " + msg.getPath() + " does not exist");
}
var size = fs.getFileSize(msg.getPath());
if (size > 100_000_000) {
var file = BlobManager.get().newBlobFile();
try (var in = fs.openInput(msg.getPath())) {
var fixedIn = new FixedSizeInputStream(new BufferedInputStream(in), size);
try (var fileOut = Files.newOutputStream(file)) {
fixedIn.transferTo(fileOut);
}
in.transferTo(OutputStream.nullOutputStream());
}
exchange.sendResponseHeaders(200, size);View on GitHub (pinned to d85ca821ba)