GraphiteEditor/Graphite · critical
Failed to send generation request
Error message
Failed to send generation request
What it means
NodeGraphExecutor::queue_execution pushes an ExecutionRequest over a std::sync::mpsc channel to the NodeRuntime worker thread that owns the graph evaluator. NodeRuntimeIO::send returns Result<(), String>, and the expect panics when the send fails — which happens exactly when the receiving NodeRuntime thread has terminated (it panicked inside node execution or was dropped during teardown). The editor main thread crashes at the next execution request (any edit or render), so this error is a secondary symptom: the real failure is whatever killed the runtime thread earlier.
Source
Thrown at editor/src/node_graph_executor.rs:125
let node_executor = Self {
futures: Default::default(),
runtime_io: NodeRuntimeIO::with_channels(request_sender, response_receiver),
node_graph_hash: 0,
current_execution_id: 0,
previous_node_to_inspect: Vec::new(),
gradient_migration: None,
gradient_migration_attempted: HashSet::new(),
};
(node_runtime, node_executor)
}
/// Execute the network by flattening it and creating a borrow stack.
fn queue_execution(&mut self, render_config: RenderConfig) -> u64 {
let execution_id = self.current_execution_id;
self.current_execution_id += 1;
let request = ExecutionRequest { execution_id, render_config };
self.runtime_io.send(GraphRuntimeRequest::ExecutionRequest(request)).expect("Failed to send generation request");
execution_id
}
pub fn update_editor_preferences(&self, editor_preferences: EditorPreferences) {
self.runtime_io
.send(GraphRuntimeRequest::EditorPreferencesUpdate(editor_preferences))
.expect("Failed to send editor preferences");
}
/// Updates the network to monitor all inputs. Useful for the testing.
#[cfg(test)]
pub(crate) fn update_node_graph_instrumented(&mut self, document: &mut DocumentMessageHandler) -> Result<Instrumented, String> {
// We should always invalidate the cache.
self.node_graph_hash = crate::application::generate_uuid();
let mut network = document.network_interface.document_network().clone();
let instrumented = Instrumented::new(&mut network);
View on GitHub (pinned to c507b35645)
Solutions
- Find the root cause first: reproduce and read the earlier panic from the NodeRuntime thread in logs — fixing that panic removes this error.
- Handle the send error instead of expecting: on Err, mark the executor degraded, surface an error message, and optionally respawn the NodeRuntime (replace_node_runtime with fresh channels).
- Guard teardown: set a shutting_down flag checked in queue_execution so late requests are dropped instead of sent into a dead channel.
- In tests, keep the runtime thread alive for the executor's lifetime or use with_channels with a receiver that is not dropped prematurely.
Example fix
// before
self.runtime_io.send(GraphRuntimeRequest::ExecutionRequest(request)).expect("Failed to send generation request");
// after
if self.runtime_io.send(GraphRuntimeRequest::ExecutionRequest(request)).is_err() {
log::error!("node runtime thread is gone; skipping execution {execution_id}");
} Defensive patterns
Strategy: fallback
Validate before calling
// NodeRuntimeIO::send already returns Result — check it instead of expecting
if self.runtime_io.send(GraphRuntimeRequest::ExecutionRequest(request)).is_err() {
// runtime thread is dead: degrade gracefully, log, optionally respawn via replace_node_runtime
return execution_id; // skip further execution bookkeeping
} Try / catch
// Rust: not a caught exception — the expect panics. Prevent by handling the Result:
match self.runtime_io.send(GraphRuntimeRequest::ExecutionRequest(request)) {
Ok(()) => {}
Err(err) => log::error!("node runtime unavailable: {err}"),
} Prevention
- Never expect on channel sends between threads; handle SendError as an expected lifecycle event.
- Join or signal the runtime thread during shutdown before the main side goes away.
- Log worker-thread panics loudly so the original crash is found, not this secondary send failure.
- In tests, pair every executor with a runtime whose receiver outlives the test body.
When it happens
Trigger: Any operation that triggers graph re-execution (document edit, transform, frame render) after the NodeRuntime thread has died: runtime_io.send(GraphRuntimeRequest::ExecutionRequest(...)).expect("Failed to send generation request") returns Err (channel disconnected) and panics on the main thread.
Common situations: A node implementation panicking inside the runtime thread (its panic kills the thread, then the next edit crashes here); application shutdown racing a final execution request; tests spawning a NodeGraphExecutor whose runtime was replaced via replace_node_runtime; double initialization dropping the receiver bound to the stored sender.
Related errors
- Failed to send editor preferences
- Failed to send response
- Star node can't be found
- Brush node does not exist
- Path node does not exist
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/ec56b1f791de6696.
Report an issue: GitHub.