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

  1. Create/fix the directory permissions first (mkdir -p <path>; chown the running user).
  2. Point RW_QUERY_LOG_PATH at a writable volume (e.g. /var/log/risingwave/query).
  3. If an existing file occupies the path, remove or rename it.
  4. 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

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


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)