OpenOptions::open
InvalidInput error for read(true).create(true)
is unclear (or check is redundant)
#140621
Labels
A-docs
Area: Documentation for any part of the project, including the compiler, standard library, and tools
A-filesystem
Area: `std::fs`
T-libs
Relevant to the library team, which will review and decide on the PR/issue.
Problem
When attempting to open a file using
std::fs::OpenOptions
with both.read(true)
and.create(true)
set, but without.write(true)
or.append(true)
, theopen()
method returns anio::Error
with kind:InvalidInput
(corresponding to EINVAL / OS error 22 on Linux).My intention was to create a marker file that indicates the process started, without writing anything into it.
MRE:
playground
Output:
Debugging & Context
Running the above code produces
Error: Os { code: 22, kind: InvalidInput, message: "Invalid argument" }.
Because this snippet was part of a larger system, I spent considerable time debugging my logic and, eventually, using
strace
to determine what was wrong.strace
confirmed that the error occurs before anyopen
oropenat
system call is attempted for the target file, indicating that the validation failure happens within the Rust standard library.Analysis of the std::fs::sys::unix::fs source reveals a check in
OpenOptions::get_creation_mode
that explicitly returnsEINVAL
ifcreate
,create_new
, ortruncate
is set withoutwrite
orappend
also being set.Crucially, the underlying Linux syscall does permit this combination, as the following C code demonstrates:
It successfully executes
open(path, O_RDONLY | O_CREAT, 0644)
.Working C Example:
Output:
Similarly, Python's standard file opening modes can create files for reading (with
"a+"
):Documentation Issue
The current documentation for
OpenOptions::open
lists potential errors, including:While the failing case is considered an invalid combination by the Rust standard library, it is not explicitly listed. The phrase "etc." does not make it clear that requesting creation inherently requires requesting write or append access within the
OpenOptions
builder—even though the underlying OS (like Linux) doesn't enforce this restriction forO_RDONLY | O_CREAT
.This can be surprising for users expecting behavior aligned with OS syscalls.
Note
The
.create()
method’s documentation does specify that.write(true)
or.append(true)
must also be set for.create(true)
to actually create a file. However, this restriction is not repeated in the.open()
documentation, nor is it reflected in error messages. This makes it easy to overlook, leading to confusion and unnecessary debugging.Suggestion
Please consider updating the documentation for
OpenOptions::open
and/or theio::ErrorKind::InvalidInput
description to explicitly state that setting.create(true)
,.create_new(true)
, or.truncate(true)
also requires.write(true)
or.append(true)
to be set. This would make the behavior much less surprising for users expecting alignment with the underlying OS.Alternatively, is this validation check actually necessary? If not, perhaps it could be relaxed to match OS behavior. At minimum, returning a more specific error message (rather than "Invalid argument") would also help prevent user confusion.
upd
removed read from mre because it is set by default
The text was updated successfully, but these errors were encountered: