{"record":{"id":"278c3d89f5e6fe0d","repo":"linera-io/linera-protocol","slug":"failed-to-open-log-file-for-writing","errorCode":null,"errorMessage":"Failed to open log file for writing","messagePattern":"Failed to open log file for writing","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-service/src/tracing/mod.rs","lineNumber":141,"sourceCode":"}\n\n/// Opens a log file for writing.\n///\n/// The location of the file is determined by the `LINERA_LOG_DIR` environment variable,\n/// and its name by the `log_name` parameter.\n///\n/// Returns [`None`] if the `LINERA_LOG_DIR` environment variable is not set.\npub(crate) fn open_log_file(log_name: &str) -> Option<File> {\n    let log_directory = env::var_os(\"LINERA_LOG_DIR\")?;\n    let mut log_file_path = Path::new(&log_directory).join(log_name);\n    log_file_path.set_extension(\"log\");\n\n    Some(\n        OpenOptions::new()\n            .append(true)\n            .create(true)\n            .open(log_file_path)\n            .expect(\"Failed to open log file for writing\"),\n    )\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nstruct WithTraceContext;\n\n#[cfg(not(target_arch = \"wasm32\"))]\nimpl<S, N> FormatEvent<S, N> for WithTraceContext\nwhere\n    S: Subscriber + for<'span> LookupSpan<'span>,\n    N: for<'writer> FormatFields<'writer> + 'static,\n{\n    fn format_event(\n        &self,\n        ctx: &fmt::FmtContext<'_, S, N>,\n        mut writer: fmt::format::Writer<'_>,\n        event: &tracing::Event<'_>,\n    ) -> std::fmt::Result {","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-service/src/tracing/mod.rs#L123-L159","documentation":"Panic during tracing initialization: `open_log_file` reads `LINERA_LOG_DIR`, joins `log_name` with a `.log` extension, and opens the result with append+create. `create(true)` only creates the file itself, never missing parent directories, so a nonexistent or unwritable LINERA_LOG_DIR (or a path that resolves to a directory) makes `open` fail and `.expect` aborts the whole process at startup. The function returns None (file logging silently disabled) only when the env var is unset.","triggerScenarios":"Starting any linera-service binary with LINERA_LOG_DIR set to a directory that does not exist; the directory being owned by another user (EACCES); LINERA_LOG_DIR naming a regular file so the joined `<dir>/<name>.log` cannot be opened; SELinux/AppArmor denying writes to the location.","commonSituations":"systemd or Docker configs setting LINERA_LOG_DIR=/var/log/linera without RuntimeDirectory= or a pre-created writable volume; running as non-root against root-owned /var/log; setups where the logs volume is no longer mounted after a migration.","solutions":["Pre-create and own the directory as the service user: `mkdir -p \"$LINERA_LOG_DIR\" && chown $(id -un) \"$LINERA_LOG_DIR\"` (or add `RuntimeDirectory=linera` to the systemd unit), then restart.","Unset LINERA_LOG_DIR if file logging is not needed — the code then skips the file layer entirely instead of panicking.","Verify as the service user: `touch \"$LINERA_LOG_DIR/probe.log\"`.","In custom builds, downgrade the expect to a warning plus None so logging init can never kill the process."],"exampleFix":"// before\nSome(\n    OpenOptions::new()\n        .append(true)\n        .create(true)\n        .open(log_file_path)\n        .expect(\"Failed to open log file for writing\"),\n)\n// after\nmatch OpenOptions::new().append(true).create(true).open(&log_file_path) {\n    Ok(file) => Some(file),\n    Err(err) => {\n        eprintln!(\"warning: cannot open log file {}: {err}; file logging disabled\", log_file_path.display());\n        None\n    }\n}","handlingStrategy":"validation","validationCode":"if let Some(dir) = std::env::var_os(\"LINERA_LOG_DIR\") {\n    std::fs::create_dir_all(&dir)\n        .with_context(|| format!(\"LINERA_LOG_DIR is not creatable: {}\", dir.to_string_lossy()))?;\n    let probe = std::path::Path::new(&dir).join(\".probe\");\n    std::fs::write(&probe, b\"\")?;\n    std::fs::remove_file(&probe)?;\n}","typeGuard":null,"tryCatchPattern":"// inside open_log_file, replacing the expect:\nmatch OpenOptions::new().append(true).create(true).open(&log_file_path) {\n    Ok(file) => Some(file),\n    Err(err) => {\n        eprintln!(\"warning: file logging disabled: cannot open {}: {err}\", log_file_path.display());\n        None\n    }\n}","preventionTips":["Pre-create the log directory in the systemd unit (RuntimeDirectory=linera) or container entrypoint before exec.","Guard wrapper scripts with `[ -d \"$LINERA_LOG_DIR\" ] && [ -w \"$LINERA_LOG_DIR\" ]` before starting the binary.","Unset LINERA_LOG_DIR in ephemeral/test environments where file logs are unwanted; that path skips file logging without panicking.","Run the service as a user that owns the logs directory."],"tags":["rust","logging","file-io","environment-variable","permissions"],"backgroundTag":"log-file-open-failed","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}