quickwit-oss/quickwit · error
read-only
Error message
read-only
What it means
The read_only_directory! macro in quickwit-directories provides a default `atomic_write` implementation for read-only tantivy Directory wrappers (e.g. StorageDirectory, UnionDirectory). Calling atomic_write on such a directory always aborts with unimplemented!("read-only") — writes are structurally impossible by design on these directories.
Source
Thrown at quickwit/quickwit-directories/src/lib.rs:44
mod bundle_directory;
mod caching_directory;
mod debug_proxy_directory;
mod hot_directory;
mod storage_directory;
mod union_directory;
pub use self::bundle_directory::{BundleDirectory, get_hotcache_from_split, read_split_footer};
pub use self::caching_directory::CachingDirectory;
pub use self::debug_proxy_directory::{DebugProxyDirectory, ReadOperation};
pub use self::hot_directory::{HotDirectory, write_hotcache};
pub use self::storage_directory::StorageDirectory;
pub use self::union_directory::UnionDirectory;
macro_rules! read_only_directory {
() => {
fn atomic_write(&self, _path: &Path, _data: &[u8]) -> io::Result<()> {
unimplemented!("read-only")
}
fn delete(&self, _path: &Path) -> Result<(), tantivy::directory::error::DeleteError> {
unimplemented!("read-only")
}
fn open_write(
&self,
_path: &Path,
) -> Result<tantivy::directory::WritePtr, tantivy::directory::error::OpenWriteError> {
unimplemented!("read-only")
}
fn sync_directory(&self) -> io::Result<()> {
unimplemented!("read-only")
}
fn watch(View on GitHub (pinned to a39730c5cd)
Solutions
- Use a writable directory (e.g. ManagedDirectory with its storage, or a temp-dir directory) for any code path that writes.
- Restructure the code so read-only directories are only used for open/read operations.
- If you only need an in-memory write target, wrap with caching/BoundingBoxDirectory semantics or RAMDirectory.
Defensive patterns
Strategy: type-guard
Validate before calling
// ensure the directory supports writes before calling:
fn supports_write<D: Directory>(dir: &D) -> bool {
std::any::type_name_of_val(dir).contains("StorageDirectory") == false // read-only macro users
} Type guard
trait WritableDirectory: Directory {}
fn require_writable(dir: &dyn Directory) -> Option<&dyn WritableDirectory> {
// narrow via downcast to the managed/writable impl
dir.as_any().downcast_ref::<ManagedDirectory>().map(|d| d as _)
} Try / catch
// unimplemented! panics; cannot be caught as an io::Error. Guard by design:
if writable_dir_for(path).is_none() { bail!("directory is read-only"); } Prevention
- Never route write operations (hotcache writes, delete-bitsets) through StorageDirectory/UnionDirectory.
- Keep a compile-time separation between reader (read-only) and writer (ManagedDirectory) directory types.
- Audit custom Directory impls for the read_only_directory! macro before wiring them into write paths.
When it happens
Trigger: Any code path that attempts `directory.atomic_write(path, data)` on a directory created via read_only_directory! (e.g. a StorageDirectory over S3 used for search).
Common situations: Tantivy trying to write hotspot metadata or a deleted-bitset during a search on a read-only split; accidentally pointing a component that needs writes at a read-only directory implementation.
Related errors
- Facet are not supported in quickwit yet.
- node not found in pending
- OTP logs or traces do not support VRL transforms
- `doc_batch` should not be empty
- timestamp_secs must be UInt64 or Int64 for MC-3 check
AI-assisted analysis of quickwit-oss/quickwit@a39730c5cd (2026-09-08).
Data as JSON: /api/errors/7a3717f65fe640a3.
Report an issue: GitHub.