linebender/druid · error

TabsPolicy::Build called on a policy that does not support…

Error message

TabsPolicy::Build called on a policy that does not support incremental building

What it means

In druid's TabsPolicy trait, the default `build` method is a placeholder that panics. Policies that can construct themselves from their Build type (like dynamic tab sources) override it; policies such as StaticTabs do not support incremental building, so calling the default impl aborts. This is an API-misuse guard, not a runtime failure of the tabs widget itself.

Solutions

  1. Do not call `build` on the policy directly; build the Tabs widget from the full data and let `Tabs::new(policy).on_data(...)` construct tabs from data
  2. Override `fn build(build: Self::Build) -> Self` in your custom TabsPolicy to construct the policy from its Build type
  3. If you need StaticTabs behavior, construct the policy directly (e.g. `StaticTabs` with its tabs field) instead of going through `build`
  4. Route updates through druid's normal data->widget rebuild path instead of manual incremental building

Example fix

// before
let policy = MyTabs::build(my_build_data); // panics for policies without build
// after
let tabs = Tabs::new(MyTabsPolicy::default())
    .with_policy(MyTabsPolicy::default())
    .on_data(|policy, data| { /* derive tabs from data */ });
Defensive patterns

Strategy: validation

Validate before calling

// Only call build on policies that support it
fn safe_build<P: TabsPolicy>(p: P, b: P::Build) -> Option<P> { None /* default build panics; require an overridden impl */ }
// Prefer: construct the Tabs widget from data instead of calling policy.build

Prevention

When it happens

Trigger: Calling `TabsPolicy::build(...)` (or a wrapper that does) on a policy that has not overridden `build` — e.g. invoking `build` on StaticTabs or any custom policy that only implements `tabs`, `tab_info` etc. but relies on the default `build`.

Common situations: Writing generic code over `impl TabsPolicy` that assumes every policy can rebuild itself; migrating code after a policy change (swapping a dynamic policy for StaticTabs); calling tabs.build() from a delegate trying to refresh tab contents.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of linebender/druid@0f8b1195e4 (2026-09-10). Data as JSON: /api/errors/47ff67492a7eba5b. Report an issue: GitHub.

Appendix: source

Thrown at druid/src/widget/tabs.rs:100

    /// Label widget for the tab.
    /// Usually implemented with a call to default_make_label ( can't default here because Self::LabelWidget isn't determined)
    fn tab_label(
        &self,
        key: Self::Key,
        info: TabInfo<Self::Input>,
        data: &Self::Input,
    ) -> Self::LabelWidget;

    /// Change the data to reflect the user requesting to close a tab.
    #[allow(unused_variables)]
    fn close_tab(&self, key: Self::Key, data: &mut Self::Input) {}

    #[allow(unused_variables)]
    /// Construct an instance of this TabsFromData from its Build type.
    /// The main use case for this is StaticTabs, where the tabs are provided by the app developer up front.
    fn build(build: Self::Build) -> Self {
        panic!("TabsPolicy::Build called on a policy that does not support incremental building")
    }

    /// A default implementation for make label, if you do not wish to construct a custom widget.
    fn default_make_label(info: TabInfo<Self::Input>) -> Label<Self::Input> {
        Label::new(info.name).with_text_color(theme::FOREGROUND_LIGHT)
    }
}

/// A TabsPolicy that allows the app developer to provide static tabs up front when building the
/// widget.
#[derive(Clone)]
pub struct StaticTabs<T> {
    // This needs be able to avoid cloning the widgets we are given -
    // as such it is Rc
    tabs: Rc<[InitialTab<T>]>,
}

impl<T> Default for StaticTabs<T> {

View on GitHub (pinned to 0f8b1195e4)