{"record":{"id":"08458ad127606d0c","repo":"janhq/jan","slug":"clone-log-file","errorCode":null,"errorMessage":"clone log file","messagePattern":"clone log file","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src-tauri/src/bin/jan-cli.rs","lineNumber":752,"sourceCode":"    argv.push(format!(\"--ctx-size={}\",     args.ctx_size));\n    argv.push(format!(\"--threads={}\",      args.threads));\n    if !args.api_key.is_empty()        { argv.push(format!(\"--api-key={}\", args.api_key)); }\n    if args.fit                        { argv.push(\"--fit\".into()); }\n    if args.verbose                    { argv.push(\"--verbose\".into()); }\n\n    // Resolve log file path\n    let log_path: PathBuf = args.log.as_deref()\n        .map(PathBuf::from)\n        .unwrap_or_else(|| cli_get_data_folder().join(\"logs\").join(\"serve.log\"));\n\n    if let Some(parent) = log_path.parent() {\n        let _ = std::fs::create_dir_all(parent);\n    }\n\n    let log_file = std::fs::OpenOptions::new()\n        .create(true).append(true).open(&log_path)\n        .unwrap_or_else(|e| { eprintln!(\"Cannot open log file {}: {e}\", log_path.display()); std::process::exit(1); });\n    let log_out = log_file.try_clone().expect(\"clone log file\");\n\n    let mut cmd = std::process::Command::new(&exe);\n    cmd.args(&argv)\n        .stdin(std::process::Stdio::null())\n        .stdout(log_out)\n        .stderr(log_file);\n\n    // Detach from the current terminal session on Unix\n    #[cfg(unix)]\n    {\n        use std::os::unix::process::CommandExt;\n        unsafe {\n            cmd.pre_exec(|| {\n                nix::unistd::setsid()\n                    .map(|_| ())\n                    .map_err(|e| std::io::Error::other(e.to_string()))\n            });\n        }","sourceCodeStart":734,"sourceCodeEnd":770,"githubUrl":"https://github.com/janhq/jan/blob/fad3f12a147d138388a66f0d92a02b2675f65294/src-tauri/src/bin/jan-cli.rs#L734-L770","documentation":"This is a panic (.expect) from File::try_clone() on the log file handle inside spawn_detached(). try_clone() duplicates the underlying file descriptor so one copy can be used for the child's stdout and the original for stderr. It fails when the OS cannot duplicate the descriptor — fd exhaustion (EMFILE), the file handle was already closed, or an I/O error at the OS level.","triggerScenarios":"Process has hit the open file descriptor limit (ulimit -n). The log file was on a filesystem that was unmounted or became read-only after opening. Resource limits inside a container (cgroup pids/fd limits). The file handle was consumed/dropped by another code path before try_clone runs.","commonSituations":"Long-running CLI sessions with many open files hitting ulimit defaults (1024). Running inside Docker with restrictive --ulimit nofile settings. Disk full or filesystem remounted read-only. SELinux/AppArmor denying dup() syscall.","solutions":["Raise the file descriptor limit: `ulimit -n 65536` before launching jan.","Ensure the log directory is on a writable, mounted filesystem.","Check for SELinux/AppArmor policies that may deny dup() syscalls.","Replace .expect with a fallback that re-opens the file or uses /dev/null."],"exampleFix":"// before\nlet log_out = log_file.try_clone().expect(\"clone log file\");\n\n// after\nlet log_out = log_file.try_clone().unwrap_or_else(|e| {\n    eprintln!(\"Warning: could not clone log fd ({e}); re-opening file\");\n    std::fs::OpenOptions::new()\n        .create(true).append(true).open(&log_path)\n        .unwrap_or_else(|_| std::fs::File::create(\"/dev/null\").unwrap())\n});","handlingStrategy":"try-catch","validationCode":"// Check fd limits before cloning\n#[cfg(unix)]\nfn check_fd_headroom() -> bool {\n    use std::mem;\n    let mut rlim = libc::rlimit { rlim_cur: 0, rlim_max: 0 };\n    unsafe {\n        if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 {\n            return true; // can't check, assume ok\n        }\n    }\n    // If soft limit is very low, cloning may fail\n    rlim.rlim_cur > 64\n}\n\nif !check_fd_headroom() {\n    eprintln!(\"Warning: low file descriptor limit; log file clone may fail.\");\n}","typeGuard":null,"tryCatchPattern":"// Replace .expect with graceful handling\nlet log_out = log_file.try_clone().unwrap_or_else(|e| {\n    eprintln!(\"Warning: could not clone log file handle ({e}); using same handle for stdout+stderr\");\n    // Fallback: try to re-open the file, or use a null sink\n    std::fs::File::create(\"/dev/null\").unwrap_or(log_file.try_clone().unwrap())\n});","preventionTips":["Raise ulimit -n before launching jan in production.","Use separate open() calls for stdout and stderr instead of try_clone to avoid fd duplication issues.","Monitor file descriptor usage in long-running sessions.","Test the detached spawn path under low-fd conditions in CI."],"tags":["panic","file-descriptor","try-clone","log-file","cli"],"backgroundTag":null,"analyzedSha":"fad3f12a147d138388a66f0d92a02b2675f65294","analyzedAt":"2026-08-12T20:33:47.516Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}