risingwavelabs/risingwave · critical
failed to create directory
Error message
failed to create directory '{}' for query log: {} What it means
When `RW_QUERY_LOG_PATH` is set, init_risingwave_logger calls `std::fs::create_dir_all` on it and panics on failure with the path and OS error. This enables per-query log files, so the directory must be creatable before logging is wired up.
Solutions
- Create/fix the directory permissions first (mkdir -p <path>; chown the running user).
- Point RW_QUERY_LOG_PATH at a writable volume (e.g. /var/log/risingwave/query).
- If an existing file occupies the path, remove or rename it.
- Unset RW_QUERY_LOG_PATH to disable query logging entirely.
Example fix
// before: read-only path RW_QUERY_LOG_PATH=/mnt/readonly/querylog // after: writable volume RW_QUERY_LOG_PATH=/var/log/risingwave/query
Defensive patterns
Strategy: validation
Validate before calling
use std::path::Path;
let p = std::env::var("RW_QUERY_LOG_PATH").unwrap_or_default();
let path = Path::new(&p);
if !p.is_empty() && (path.exists() && !path.is_dir()) {
panic!("RW_QUERY_LOG_PATH is not a directory: {p}");
} Prevention
- Pre-provision and chown the log directory in your container image or entrypoint.
- Mount a writable volume at the configured path.
- Verify the path is a directory, not an existing file, before startup.
When it happens
Trigger: RW_QUERY_LOG_PATH points somewhere create_dir_all cannot create: permission-denied, a parent component is a file, read-only filesystem, or invalid path characters.
Common situations: Pointing at a root-owned or non-writable volume in containers; path collides with an existing file; mounting a read-only volume at that path; typo making an impossible deep path.
Understand the failure class
Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.
Related errors
- failed to create
- failed to parse `RUST_LOG`
- AZBLOB_ENDPOINT not found from environment variables
- Failed to get available parallelism
- Failed to parse RW_DEADLOCK_DETECTION
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/dda28577543129c4.
Report an issue: GitHub.
Appendix: source
Thrown at src/utils/runtime/src/logger.rs:365
} else {
fmt_layer.boxed()
}
}
};
layers.push(
fmt_layer
.with_filter(default_filter.clone().with_target("rw_tracing", Level::OFF)) // filter-out tracing-only events
.boxed(),
);
};
// If `RW_QUERY_LOG_PATH` env var is set to a directory, turn on query log files.
let query_log_path = std::env::var("RW_QUERY_LOG_PATH");
if let Ok(query_log_path) = query_log_path {
let query_log_path = PathBuf::from(query_log_path);
std::fs::create_dir_all(query_log_path.clone()).unwrap_or_else(|e| {
panic!(
"failed to create directory '{}' for query log: {}",
query_log_path.display(),
e.as_report(),
)
});
/// Newtype wrapper for `DefaultFields`.
///
/// `fmt::Layer` will share the same `FormattedFields` extension for spans across
/// different layers, as long as the type of `N: FormatFields` is the same. This
/// will cause several problems:
///
/// - `with_ansi(false)` does not take effect and it will follow the settings of
/// the primary fmt layer installed above.
/// - `Span::record` will update the same `FormattedFields` multiple times,
/// leading to duplicated fields.
///
/// As a workaround, we use a newtype wrapper here to get a different type id.View on GitHub (pinned to 6469eb736d)