janhq/jan · critical
clone log file
Error message
clone log file
What it means
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.
Source
Thrown at src-tauri/src/bin/jan-cli.rs:752
argv.push(format!("--ctx-size={}", args.ctx_size));
argv.push(format!("--threads={}", args.threads));
if !args.api_key.is_empty() { argv.push(format!("--api-key={}", args.api_key)); }
if args.fit { argv.push("--fit".into()); }
if args.verbose { argv.push("--verbose".into()); }
// Resolve log file path
let log_path: PathBuf = args.log.as_deref()
.map(PathBuf::from)
.unwrap_or_else(|| cli_get_data_folder().join("logs").join("serve.log"));
if let Some(parent) = log_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
let log_file = std::fs::OpenOptions::new()
.create(true).append(true).open(&log_path)
.unwrap_or_else(|e| { eprintln!("Cannot open log file {}: {e}", log_path.display()); std::process::exit(1); });
let log_out = log_file.try_clone().expect("clone log file");
let mut cmd = std::process::Command::new(&exe);
cmd.args(&argv)
.stdin(std::process::Stdio::null())
.stdout(log_out)
.stderr(log_file);
// Detach from the current terminal session on Unix
#[cfg(unix)]
{
use std::os::unix::process::CommandExt;
unsafe {
cmd.pre_exec(|| {
nix::unistd::setsid()
.map(|_| ())
.map_err(|e| std::io::Error::other(e.to_string()))
});
}View on GitHub (pinned to fad3f12a14)
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.
Example fix
// before
let log_out = log_file.try_clone().expect("clone log file");
// after
let log_out = log_file.try_clone().unwrap_or_else(|e| {
eprintln!("Warning: could not clone log fd ({e}); re-opening file");
std::fs::OpenOptions::new()
.create(true).append(true).open(&log_path)
.unwrap_or_else(|_| std::fs::File::create("/dev/null").unwrap())
}); Defensive patterns
Strategy: try-catch
Validate before calling
// Check fd limits before cloning
#[cfg(unix)]
fn check_fd_headroom() -> bool {
use std::mem;
let mut rlim = libc::rlimit { rlim_cur: 0, rlim_max: 0 };
unsafe {
if libc::getrlimit(libc::RLIMIT_NOFILE, &mut rlim) != 0 {
return true; // can't check, assume ok
}
}
// If soft limit is very low, cloning may fail
rlim.rlim_cur > 64
}
if !check_fd_headroom() {
eprintln!("Warning: low file descriptor limit; log file clone may fail.");
} Try / catch
// Replace .expect with graceful handling
let log_out = log_file.try_clone().unwrap_or_else(|e| {
eprintln!("Warning: could not clone log file handle ({e}); using same handle for stdout+stderr");
// Fallback: try to re-open the file, or use a null sink
std::fs::File::create("/dev/null").unwrap_or(log_file.try_clone().unwrap())
}); Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cannot resolve current exe
- Failed to determine the home directory
- Failed to serialize MCP settings
- Failed to get current exe path
- Executable must have a parent directory
AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12).
Data as JSON: /api/errors/08458ad127606d0c.
Report an issue: GitHub.