oracle/graal · error · IllegalArgumentException
READ + APPEND not allowed
Error message
READ + APPEND not allowed
What it means
Thrown by TruffleFileSystemProvider.newFileChannel when the option set contains both READ and APPEND. Opening for append positions every write at the end of the file, which is meaningless for a channel that is also readable, so the combination is rejected up front exactly as the JDK's sun.nio.fs providers do. Note that APPEND already implies WRITE.
Source
Thrown at espresso/src/com.oracle.truffle.espresso.io/src/sun/nio/fs/TruffleFileSystemProvider.java:152
if (!readable && !writable) {
if (append) {
writable = true;
} else {
readable = true;
}
}
// set direct option
boolean direct = false;
for (OpenOption option : options) {
if (ExtendedOptions.DIRECT.matches(option)) {
direct = true;
break;
}
}
// check for Exceptions
if (readable && append) {
throw new IllegalArgumentException("READ + APPEND not allowed");
}
if (append && options.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed");
}
FileDescriptor fd = new FileDescriptor();
// populates fd via HostCode which opens the file and checks the permissions
newFileChannel0(TrufflePath.toTrufflePath(path), fd, openOptionsMask, fileAttributeMask);
return NewFileChannelHelper.open(fd, path.toString(), readable, writable, sync, direct, null);
}
@Override
@SuppressWarnings("unchecked")
public void createDirectory(Path dir, FileAttribute<?>... attrs) throws IOException {
// ALL_PERMISSIONS is the default permission on Unix.
int fileAttributeMask = FileAttributeParser.parseWithDefault(FileAttributeParser.ALL_PERMISSIONS, attrs);
createDirectory0(TrufflePath.toTrufflePath(dir), fileAttributeMask);
}View on GitHub (pinned to a66e9ccd1d)
Solutions
- Drop READ and open with just APPEND (it implies WRITE) if you only append
- Open with READ + WRITE and seek to the end manually if you need to read back what you wrote
- Build the option set conditionally so READ is never added when APPEND is present
Example fix
// before FileChannel ch = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.APPEND); // after FileChannel ch = FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.APPEND); // or, to read and append: FileChannel rw = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.WRITE); rw.position(rw.size());
Defensive patterns
Strategy: validation
Validate before calling
Set<OpenOption> opts = ...;
if (opts.contains(StandardOpenOption.READ) && opts.contains(StandardOpenOption.APPEND)) {
opts.remove(StandardOpenOption.READ); // APPEND implies WRITE
} Try / catch
catch (IllegalArgumentException e) when message contains 'READ + APPEND': rethrow as a configuration error naming the offending option set.
Prevention
- Encode open modes as an enum (READ, WRITE, APPEND) and expand to options in exactly one place
- Unit-test the option expansion for all modes
When it happens
Trigger: Files.newByteChannel(path, StandardOpenOption.READ, StandardOpenOption.APPEND) or Files.newFileChannel(...) with both flags, executed against a TrufflePath in the Espresso guest.
Common situations: Log-rotation code that opens a file with a generic option list built at runtime and accidentally unions READ into an append set; refactoring code that previously used RandomAccessFile.
Related errors
- APPEND + TRUNCATE_EXISTING not allowed
- open option: {}
- 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/9a53deac1e6223a0.
Report an issue: GitHub.