GraphiteEditor/Graphite · error
Artboard should have a primary input
Error message
Artboard should have a primary input
What it means
Panics when an `Artboard` node fetched from the document network has an empty `inputs` vec, so `.inputs.first()` returns `None`. The handler (`GraphOperationMessage::CreateArtboard` flow) assumes every artboard node carries at least a primary input; the template registered for `"Artboard"` in `DOCUMENT_NODE_TYPES` defines that input. The expect guards against definition drift where the Artboard node template is created or serialized with zero inputs.
Source
Thrown at editor/src/messages/portfolio/document/graph_operation/graph_operation_message_handler.rs:206
id,
location,
dimensions,
background,
clip,
} => {
let mut modify_inputs = ModifyInputsContext::new(network_interface, responses);
let artboard_layer = modify_inputs.create_artboard(id, location, dimensions, background, clip);
network_interface.move_layer_to_stack(artboard_layer, LayerNodeIdentifier::ROOT_PARENT, 0, &[]);
// If there is a non artboard feeding into the primary input of the artboard, move it to the secondary input
let Some(artboard) = network_interface.document_network().nodes.get(&id) else {
log::error!("Artboard not created");
return;
};
let document_metadata = network_interface.document_metadata();
let primary_input = artboard.inputs.first().expect("Artboard should have a primary input").clone();
if let NodeInput::Node { node_id, .. } = &primary_input {
if network_interface.is_artboard(node_id, &[]) {
// Nothing to do here: we have a stack full of artboards!
} else if network_interface.is_layer(node_id, &[]) {
// We have a stack of non-layer artboards.
for (insert_index, layer) in LayerNodeIdentifier::ROOT_PARENT.children(document_metadata).filter(|&layer| layer != artboard_layer).enumerate() {
// Parent the layer to our new artboard (retaining ordering)
responses.add(NodeGraphMessage::MoveLayerToStack {
layer,
parent: artboard_layer,
insert_index,
});
// Apply a translation to prevent the content from shifting
responses.add(GraphOperationMessage::TransformChange {
layer,
transform: DAffine2::from_translation(-location),
transform_in: TransformIn::Local,
skip_rerender: true,View on GitHub (pinned to c507b35645)
Solutions
- Check the `"Artboard"` definition in `document_node_definitions.rs` — its `node_template.inputs` must contain at least the primary `Artboard`-typed input; restore it if a refactor removed it.
- If loading user files, validate/repair the deserialized node before this handler runs (reject or re-template artboards with zero inputs).
- Replace `expect` with `let-else`: log `"Artboard has no primary input"` and return early so one bad node cannot crash the document.
- Add a test asserting `resolve_network_node_type("Artboard").node_template.inputs` is non-empty to catch registry drift at CI time.
Example fix
// before
let primary_input = artboard.inputs.first().expect("Artboard should have a primary input").clone();
// after
let Some(primary_input) = artboard.inputs.first() else {
log::error!("Artboard node {id} has no primary input; skipping input reshuffle");
return;
};
let primary_input = primary_input.clone(); Defensive patterns
Strategy: validation
Validate before calling
// Before creating an artboard, verify the definition provides a primary input:
let has_primary_input = document_node_definitions::resolve_network_node_type("Artboard")
.is_some_and(|def| !def.node_template.inputs.is_empty());
if !has_primary_input {
log::error!("Artboard definition lacks a primary input; aborting artboard creation");
return;
} Type guard
fn node_has_primary_input(node: &DocumentNode) -> bool {
!node.inputs.is_empty()
} Try / catch
// Rust has no try/catch; convert the invariant into a checked branch:
match artboard.inputs.first() {
Some(primary_input) => { /* existing logic */ }
None => log::error!("Artboard {} has no primary input", id),
} Prevention
- Keep a unit test asserting the Artboard template has a non-empty inputs vec
- Validate deserialized documents: reject or repair artboard nodes with zero inputs before handlers run
- When changing the Artboard definition template, run the artboard-creation integration test
- Use let-else instead of expect for any structural assumption about deserialized nodes
When it happens
Trigger: Creating an artboard (or artboard-containing document import) triggers `create_artboard` + this handler. The panic fires if the node stored at `id` has no inputs: a hand-edited/corrupted `.graphite` file, a deserialization path that builds an Artboard node with no template inputs, or an Artboard `DocumentNodeDefinition` whose `node_template.inputs` was emptied during a registry refactor.
Common situations: Editing the Artboard definition's template and removing/reordering the primary input; loading old documents serialized against a different node schema; programmatic document construction that inserts an Artboard node via a raw template instead of `resolve_network_node_type("Artboard")`.
Related errors
- Node
- Solidify Stroke node should exist
- In `check_layer()`: there should be a `target`
- Merge node
- Boolean node does not exist
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/e8747c14d9a52690.
Report an issue: GitHub.