glzr-io/glazewm · error

Cannot use an already attached container as replacement…

Error message

Cannot use an already attached container as replacement container.

What it means

`replace_container` swaps an existing child for a replacement, requiring the replacement to be detached. If the replacement container is already attached elsewhere in the tree, it would end up with two parents, so the function bails.

Solutions

  1. Detach the replacement container before calling `replace_container`
  2. Verify with `replacement_container.is_detached()` before the call
  3. Rework the caller so the replacement is created or detached fresh

Example fix

// before
replace_container(&replacement, &parent, index)?;
// after
detach_container(&replacement);
replace_container(&replacement, &parent, index)?;
Defensive patterns

Strategy: validation

Validate before calling

if !replacement.is_detached() {
  detach_container(&replacement);
}
replace_container(&replacement, &target_parent, index)?;

Try / catch

match replace_container(&replacement, &parent, idx) {
  Err(e) if e.to_string().contains("already attached") => { /* detach and retry */ }
  r => r?,
}

Prevention

When it happens

Trigger: Passing a container that currently has a parent as `replacement_container` — e.g. replacing a container with another live window's container without detaching it first.

Common situations: Command handlers implementing swap/replace semantics that reuse an attached container reference, or event handlers reusing a container that was never detached.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of glzr-io/glazewm@5709ad0a3c (2026-09-08). Data as JSON: /api/errors/d664a973cb6e6596. Report an issue: GitHub.

Appendix: source

Thrown at packages/wm/src/commands/container/replace_container.rs:19

use anyhow::{bail, Context};
use wm_common::VecDequeExt;

use super::{attach_container, detach_container, resize_tiling_container};
use crate::{
  models::Container,
  traits::{CommonGetters, TilingSizeGetters},
};

/// Replaces a container at the specified index.
///
/// The replaced container will be detached from the tree.
pub fn replace_container(
  replacement_container: &Container,
  target_parent: &Container,
  target_index: usize,
) -> anyhow::Result<()> {
  if !replacement_container.is_detached() {
    bail!(
      "Cannot use an already attached container as replacement container."
    );
  }

  let container_to_replace = target_parent
    .children()
    .get(target_index)
    .cloned()
    .with_context(|| format!("No container at index {target_index}."))?;

  let focus_index = container_to_replace.focus_index();
  let tiling_size = container_to_replace
    .as_tiling_container()
    .map(|c| c.tiling_size());

  // TODO: This will cause issues if the detach causes a wrapping split
  // container to flatten. Currently, that scenario shouldn't be possible.
  // We also can't attach first before detaching, because detaching

View on GitHub (pinned to 5709ad0a3c)