oracle/graal · error · UnsupportedOperationException
open option: {}
Error message
open option: {} What it means
Thrown by TruffleFileSystemProvider.openOptionsToMask when an OpenOption in the set is not in SUPPORTED_OPEN_OPTIONS. The provider supports READ, WRITE, APPEND, TRUNCATE_EXISTING, CREATE, CREATE_NEW, DELETE_ON_CLOSE, SPARSE, SYNC, DSYNC, and LinkOption.NOFOLLOW_LINKS; the list must stay in sync with the guest-side Target_*_TruffleFileSystemProvider. Anything else (a custom OpenOption) has no bitmask slot.
Source
Thrown at espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java:224
private static int copyOptionsToMask(CopyOption... options) {
int mask = 0;
for (CopyOption option : options) {
int index = SUPPORTED_COPY_OPTIONS.indexOf(option);
if (index < 0) {
throw new UnsupportedOperationException("copy option: " + option);
}
assert index < 32;
mask |= 1 << index;
}
return mask;
}
private static int openOptionsToMask(Set<? extends OpenOption> options) {
int mask = 0;
for (OpenOption option : options) {
int index = SUPPORTED_OPEN_OPTIONS.indexOf(option);
if (index < 0) {
throw new UnsupportedOperationException("open option: " + option);
}
mask |= 1 << index;
}
return mask;
}
@Override
public void copy(Path source, Path target, CopyOption... options) throws IOException {
copy0(TrufflePath.toTrufflePath(source), TrufflePath.toTrufflePath(target), copyOptionsToMask(options));
}
@Override
public void move(Path source, Path target, CopyOption... options) throws IOException {
move0(TrufflePath.toTrufflePath(source), TrufflePath.toTrufflePath(target), copyOptionsToMask(options));
}
@Override
public boolean isSameFile(Path path, Path path2) throws IOException {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Remove the custom OpenOption from the set before opening the channel
- Check option instanceof StandardOpenOption or LinkOption.NOFOLLOW_LINKS in generic wrappers and drop unknown ones
- For unsupported behavior (e.g. custom buffering), wrap the resulting channel instead of using an option
Example fix
// before Files.newByteChannel(path, Set.of(CREATE, WRITE, new DirectIoOption())); // after Files.newByteChannel(path, Set.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
Defensive patterns
Strategy: validation
Validate before calling
private static final Set<OpenOption> TRUFFLE_OPEN_OPTIONS = Set.of(
StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.APPEND,
StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE, StandardOpenOption.CREATE_NEW,
StandardOpenOption.DELETE_ON_CLOSE, StandardOpenOption.SPARSE, StandardOpenOption.SYNC,
StandardOpenOption.DSYNC, LinkOption.NOFOLLOW_LINKS);
options.removeIf(o -> !TRUFFLE_OPEN_OPTIONS.contains(o)); Try / catch
catch (UnsupportedOperationException e) when message starts with 'open option': retry with options filtered to the eleven supported constants.
Prevention
- Do not invent custom OpenOption implementations for code that must run on the Truffle FS
- Keep option filtering in one wrapper around Files.newByteChannel
When it happens
Trigger: Files.newByteChannel/newFileChannel/newOutputStream/newInputStream on a TrufflePath with a user-defined OpenOption implementation, since all eleven standard StandardOpenOption values are supported.
Common situations: Libraries that define their own OpenOption (e.g. for direct-I/O or compression hints) and pass it through Files.*; code migrating from another provider that accepts extended options.
Related errors
- READ + APPEND not allowed
- APPEND + TRUNCATE_EXISTING not allowed
- copy option: {}
- access mode: {}
- Path component is undefined
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/f3bf6e86d5358cff.
Report an issue: GitHub.