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.

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)

Solutions

  1. Normalize the path to absolute before calling: prefix with the known base directory
  2. Use Path.toAbsolutePath() (or equivalent) on the client side against the intended base dir
  3. For remote systems, anchor to a known root such as '/' or the user's home obtained from the shell
  4. 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

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


AI-assisted analysis of xpipe-io/xpipe@d85ca821ba (2026-09-06). Data as JSON: /api/errors/c072b9df166b496f. Report an issue: GitHub.