{"record":{"id":"3a48185c7c1255a1","repo":"facebook/flow","slug":"failed-to-open-log-file","errorCode":null,"errorMessage":"Failed to open log file '{}': {}","messagePattern":"Failed to open log file '(.+?)': (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_server/src/server_daemon.rs","lineNumber":246,"sourceCode":"            }\n            fs::rename(file, &old_file)?;\n            Ok(())\n        })() {\n            eprintln!(\n                \"Log rotate: failed to move '{}' to '{}'\\n{}\",\n                file, old_file, e\n            );\n        }\n    }\n    fs::OpenOptions::new()\n        .create(true)\n        .append(true)\n        .open(file)\n        .map_err(|e| format!(\"Failed to open log file '{}': {}\", file, e))\n}\n\npub fn open_log_file(file: &str) -> fs::File {\n    try_open_log_file(file).unwrap_or_else(|e| panic!(\"{}\", e))\n}\npub fn daemonize(\n    init_id: &str,\n    log_file: &str,\n    argv: &[String],\n    lazy_mode: Option<String>,\n    no_flowlib: bool,\n    ignore_version: bool,\n    options: Arc<Options>,\n    file_watcher_pid: Option<u32>,\n    start_cause: flow_server_env::server_status::StartCause,\n    cli_overrides: &CliOverrides,\n) -> Result<Handle<(), ()>, String> {\n    let entry = registered_entry_point();\n\n    let root = &options.root;\n    let tmp_dir = &options.temp_dir;\n    let flowconfig_name = &options.flowconfig_name;","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_server/src/server_daemon.rs#L228-L264","documentation":"When the Flow server daemonizes, open_log_file() opens its log with create+append. try_open_log_file() first rotates an existing log to <file>.old (best-effort) and then opens the target; any open error is turned into this message and open_log_file panics on it. The log lives under the server temp dir, so failures here are almost always temp-dir permission or existence problems.","triggerScenarios":"daemonize()/open_log_file() running when the log path's directory does not exist, the daemon user cannot write there, the path component is a directory, SELinux/AppArmor denies the append, or the filesystem is full/read-only.","commonSituations":"TMPDIR or the temp_dir option pointing to a deleted directory; running the daemon under a different user than the one that created the temp dir; root-owned log files left behind by a previous sudo run; read-only container mounts; full disks.","solutions":["From the panic message, check the log directory: mkdir -p the parent and chown/chmod it to the daemon user.","Remove stale root-owned log and .old files: rm <file> <file>.old under the temp dir.","Set temp_dir (or TMPDIR) to a location the daemon user can write and restart.","Check df -h and mount ro flags when permissions look correct."],"exampleFix":"# before: temp dir missing / owned by another user\nflow daemon start ...  # panic: Failed to open log file '/tmp/flow/x/log': ... \n\n# after\nmkdir -p /tmp/flow/x && chown \"$(id -un)\" /tmp/flow/x && flow daemon start ...","handlingStrategy":"validation","validationCode":"// before daemonize()/open_log_file(file)\nlet dir = std::path::Path::new(file).parent().ok_or(\"log path has no parent\")?;\nstd::fs::create_dir_all(dir)?;\nlet f = std::fs::OpenOptions::new().create(true).append(true).open(file)?;\ndrop(f); // proves the real open will succeed","typeGuard":null,"tryCatchPattern":"match flow_server::server_daemon::try_open_log_file(file) {\n    Ok(f) => f,\n    Err(e) => {\n        eprintln!(\"cannot open log, falling back to stderr logging: {e}\");\n        // fall back or abort with a clean message instead of the panic\n        return Err(e);\n    }\n}","preventionTips":["Pre-create and chown the log directory in your service unit before starting the daemon.","Run the daemon as the user that owns the temp dir; avoid sudo/non-sudo mixes.","Clean up stale .old logs and root-owned files during provisioning.","Alert on temp-dir disk usage; append-mode opens fail when the volume is full or read-only."],"tags":["logging","daemon","filesystem","permissions","server-startup","temp-dir"],"backgroundTag":"file-permission-denied","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}