oracle/graal · error · UnsupportedOperationException
copy option: {}
Error message
copy option: {} What it means
Thrown by TruffleFileSystemProvider.copyOptionsToMask when a CopyOption passed to copy() or move() is not in SUPPORTED_COPY_OPTIONS. Only REPLACE_EXISTING, COPY_ATTRIBUTES, ATOMIC_MOVE, and LinkOption.NOFOLLOW_LINKS are forwarded to the native layer; any other implementation of CopyOption (the interface has only those standard constants) cannot be represented in the option bitmask.
Source
Thrown at espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java:211
StandardOpenOption.DELETE_ON_CLOSE,
StandardOpenOption.SPARSE,
StandardOpenOption.SYNC,
StandardOpenOption.DSYNC,
LinkOption.NOFOLLOW_LINKS);
// Keep in sync with Target_*_TruffleFileSystemProvider#SUPPORTED_COPY_OPTIONS.
private static final List<CopyOption> SUPPORTED_COPY_OPTIONS = List.of(
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.ATOMIC_MOVE,
LinkOption.NOFOLLOW_LINKS);
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;
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Restrict options to StandardCopyOption.REPLACE_EXISTING / COPY_ATTRIBUTES / ATOMIC_MOVE and LinkOption.NOFOLLOW_LINKS
- If ATOMIC_MOVE is rejected downstream on cross-store moves, retry without it
- Filter unknown options out (with a warning) before calling copy/move in generic utility code
Example fix
// before Files.copy(src, dst, new MyFastCopyOption()); // custom CopyOption // after Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
Defensive patterns
Strategy: validation
Validate before calling
private static final Set<CopyOption> TRUFFLE_COPY_OPTIONS = Set.of(
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES,
StandardCopyOption.ATOMIC_MOVE, LinkOption.NOFOLLOW_LINKS);
for (CopyOption o : options) {
if (!TRUFFLE_COPY_OPTIONS.contains(o)) throw new UnsupportedOperationException("copy option: " + o);
} Try / catch
catch (UnsupportedOperationException e) { log and retry Files.copy(src, dst) with no options; } Prevention
- In generic file utilities, filter options against provider.getSupportedFileAttributeViews()/known constants before copying
- Remember ATOMIC_MOVE support may still fail at runtime on cross-store moves
When it happens
Trigger: Calling Files.copy/Files.move on TrufflePaths with a custom CopyOption implementation, or a third-party library passing its own option objects.
Common situations: Cross-filesystem utilities written against other providers (e.g. a custom extended-attributes copy option) reused against the Truffle file system; option objects from a different class loader failing equals() against the standard constants.
Related errors
- open option: {}
- access mode: {}
- Path component is undefined
- Path component should be '/'
- Query component present
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/89dc865f26043389.
Report an issue: GitHub.