slint-ui/slint · error

element without a size

Error message

element without a size

What it means

`element_size` resolves an element's width/height binding via `geometry_prop`, which relies on the `default_geometry` compiler pass leaving a size binding on every element. The panic means an element reached codegen without a resolvable size binding.

Solutions

  1. Ensure the `default_geometry` pass runs before slint-sc codegen
  2. If you added a new element/builtin, teach `default_geometry` to emit a size binding for it
  3. Inspect the element with the missing binding using `SLINT_DEBUG` or a debugger on `geometry_prop` and file a compiler bug with the .slint reproduction

Example fix

// before
let resolve = |prop| geometry_prop(elem, ctx, prop).expect("element without a size");
// after
let resolve = |prop| geometry_prop(elem, ctx, prop)
    .ok_or_else(|| anyhow!("element {:?} has no size binding; ensure default_geometry ran", elem.debug_id))?;
Defensive patterns

Strategy: validation

Validate before calling

if geometry_prop(elem, ctx, |g: &GeometryProps| &g.width).is_none() {
    return Err("element has no size binding; default_geometry pass missing or incomplete");
}

Prevention

When it happens

Trigger: Compiling a .slint component where an element's width/height binding was not produced by `default_geometry` — e.g. running the slint-sc generator on a compilation unit that skipped that pass, or an element type the pass does not cover.

Common situations: Embedding the compiler in a tool and calling generator passes in the wrong order; adding a new builtin element and forgetting to teach `default_geometry` about its size.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of slint-ui/slint@bb937076de (2026-09-16). Data as JSON: /api/errors/8a8ebb284bd31c74. Report an issue: GitHub.

Appendix: source

Thrown at internal/compiler/generator/slint_sc.rs:971

fn geometry_prop(
    elem: &ElementRc,
    ctx: &Ctx,
    prop: fn(&GeometryProps) -> &NamedReference,
) -> Option<TokenStream> {
    let props = elem.borrow().geometry_props.clone();
    compile_property_reference(prop(props.as_ref()?), ctx)
}

/// The compiled offset of an element from its parent.
fn element_position(elem: &ElementRc, ctx: &Ctx) -> (TokenStream, TokenStream) {
    let resolve = |prop| geometry_prop(elem, ctx, prop).unwrap_or_else(|| quote!(0i32));
    (resolve(|g: &GeometryProps| &g.x), resolve(|g: &GeometryProps| &g.y))
}

/// The compiled size of an element. The default_geometry pass leaves a size
/// binding on every element, and the root's size resolves to the window size.
fn element_size(elem: &ElementRc, ctx: &Ctx) -> (TokenStream, TokenStream) {
    let resolve = |prop| geometry_prop(elem, ctx, prop).expect("element without a size");
    (resolve(|g: &GeometryProps| &g.width), resolve(|g: &GeometryProps| &g.height))
}

/// Walk `elem` and its descendants, emitting for each a block that adds the
/// element's position to the running `offset_x`/`offset_y`, whatever `body`
/// makes of the element, and then the children's blocks — so that later and
/// deeper elements come after earlier and shallower ones. A subtree `body`
/// makes nothing of emits nothing, and compiles nothing either, so that its
/// bindings make no coverage points.
///
/// Rendering and hit testing are the same walk, which is what makes a
/// `TouchArea` sit exactly where it paints.
fn emit_tree(
    elem: &ElementRc,
    ctx: &Ctx,
    body: &mut dyn FnMut(&ElementRc) -> Option<TokenStream>,
) -> TokenStream {
    let statements = body(elem);

View on GitHub (pinned to bb937076de)