GraphiteEditor/Graphite · error
Failed to create control channel
Error message
Failed to create control channel
What it means
Panic when ipc_channel::ipc::channel::<HostControlMessage>() fails. This call creates the OS-backed channel (socketpair on Unix, named pipe on Windows) that the host sends to the main process inside its Hello message for reverse-direction control messages. Channel creation fails when the process is out of file descriptors (EMFILE), when kernel object limits are hit, or when the sandbox blocks the underlying syscall.
Solutions
- Raise the file-descriptor limit for the app (ulimit -n before launch, LimitNOFILE= in systemd, --ulimit nofile= in docker)
- Inspect /proc/<host-pid>/fd to count open descriptors and find leaks if the failure appears over time
- Restart the app to release leaked descriptors and confirm the limit was the cause
- If sandboxed, allow the socketpair syscall family in the security policy
Example fix
# before (shell) ./graphite # host panics: Failed to create control channel # after ulimit -n 4096 ./graphite
Defensive patterns
Strategy: retry
Validate before calling
// Cheap canary: if fd count is near the limit, channel creation is likely to fail
fn fd_headroom(threshold: usize) -> bool {
let open = std::fs::read_dir("/proc/self/fd").map(|d| d.count()).unwrap_or(0);
open < threshold
} Try / catch
let (control_sender, control_receiver) = match ipc_channel::ipc::channel::<HostControlMessage>() {
Ok(ch) => ch,
Err(e) if e.to_string().contains("Too many open files") => {
std::thread::sleep(Duration::from_millis(50));
ipc_channel::ipc::channel::<HostControlMessage>().expect("control channel creation failed after retry")
}
Err(e) => panic!("Failed to create control channel: {e}"),
}; Prevention
- Launch the app with a generous file-descriptor limit (LimitNOFILE / ulimit -n 4096+)
- Track open descriptor counts in long sessions to catch leaks before they become panics
- Retry once on EMFILE-style errors; fd exhaustion can be transient under load
When it happens
Trigger: The UI host process exhausting its fd limit because many CEF browsers/pipes/sockets are open; a low ulimit -n in the service/container the host runs under; a seccomp/LD_PRELOAD sandbox interfering with socketpair creation.
Common situations: Long editor sessions accumulating leaked file descriptors; containers or systemd services started with LimitNOFILE low; heavy multi-window usage spawning many IPC channels; CI environments with restrictive rlimits.
Related errors
- Failed to connect to the main process bootstrap server
- failed to construct async-message tokio runtime
- Failed to send Hello to the main process
- Failed to spawn socket thread
- Failed to spawn the CEF control thread
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/289cb074d1be7cf5.
Report an issue: GitHub.
Appendix: source
Thrown at desktop/ui/src/remote/host.rs:31
use crate::platform::mac;
pub(crate) fn run() {
// Ignore SIGINT, the controlling process is responsible for shutting down the host
#[cfg(any(target_os = "linux", target_os = "macos"))]
unsafe {
libc::signal(libc::SIGINT, libc::SIG_IGN);
}
let args: Vec<String> = std::env::args().collect();
let config = HostConfig::from_args(&args).expect("CEF host started without a valid host config argument");
let acceleration_requested = config.acceleration;
#[cfg(target_os = "macos")]
mac::spawn_parent_watchdog(config.main_pid);
let bootstrap = IpcSender::<EventMessage>::connect(config.server.clone()).expect("Failed to connect to the main process bootstrap server");
let event_sender = Arc::new(Mutex::new(bootstrap));
let (control_sender, control_receiver) = ipc_channel::ipc::channel::<HostControlMessage>().expect("Failed to create control channel");
#[cfg(feature = "accelerated_paint")]
let plane = if acceleration_requested { PlaneSender::from_config(&config, event_sender.clone()) } else { None };
#[cfg(feature = "accelerated_paint")]
let acceleration = plane.is_some();
#[cfg(not(feature = "accelerated_paint"))]
let acceleration = {
if acceleration_requested {
tracing::error!("UI acceleration requested but the accelerated_paint feature is disabled; using software frames");
}
false
};
event_sender
.lock()
.expect("The host message sender cannot be poisoned before threads exist")
.send(EventMessage::Hello {
pid: std::process::id(),View on GitHub (pinned to c507b35645)