glzr-io/glazewm · error

Cannot run command because subject container is detached.

Error message

Cannot run command because subject container is detached.

What it means

`Wm::run_command` refuses to operate on a container that has been detached from the window-management tree. Detached containers (closed windows, removed workspaces, nodes pending cleanup) have no valid position or siblings, so mutating them is undefined. The command is aborted with this bail to protect tree invariants.

Solutions

  1. Check `subject_container.is_detached()` before invoking the command and skip or re-resolve the target.
  2. Re-resolve the target from current state (e.g. focused window) at command execution time instead of caching it.
  3. If driven by IPC, validate that the window/container ID still exists before sending the command.
  4. Handle gracefully: treat the bail as a no-op for transient races (window closed concurrently) rather than a fatal error.
  5. Ensure detach/cleanup paths don't leave stale references in queues or focus history.

Example fix

// before
wm.run_command(state, &command, &subject_container).await?;
// after
if subject_container.is_detached() {
  tracing::warn!("Skipping command; subject container detached.");
  return Ok(());
}
wm.run_command(state, &command, &subject_container).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn command_applicable(c: &Container) -> bool { !c.is_detached() }

Type guard

fn as_attached(c: &Container) -> Option<&Container> { if c.is_detached() { None } else { Some(c) } }

Try / catch

match wm.run_command(state, &cmd, &subject).await {
  Err(e) if e.to_string().contains("detached") => tracing::warn!("Target detached; skipping"),
  other => other?,
}

Prevention

When it happens

Trigger: An `InvokeCommand` arrives whose subject container was detached between selection and execution — e.g. the target window closed after focus moved, a race between an event handler detaching the container and the command processor acting on a stale handle, or a cached `ContainerRef` reused after a relayout.

Common situations: IPC/automation scripts sending commands to a window handle that was just closed; keybindings firing right after the focused window exits; async command queues holding stale container references across event-loop ticks.

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/a94e1830c936a4d3. Report an issue: GitHub.

Appendix: source

Thrown at packages/wm/src/wm.rs:230

    }

    Ok(current_subject_container.id())
  }

  #[allow(clippy::too_many_lines)]
  pub fn run_command(
    command: &InvokeCommand,
    subject_container: Container,
    state: &mut WmState,
    config: &mut UserConfig,
  ) -> anyhow::Result<()> {
    // No-op if WM is currently paused.
    if state.is_paused && *command != InvokeCommand::WmTogglePause {
      return Ok(());
    }

    if subject_container.is_detached() {
      bail!("Cannot run command because subject container is detached.");
    }

    match &command {
      InvokeCommand::AdjustBorders(args) => {
        match subject_container.as_window_container() {
          Ok(window) => {
            let args = args.clone();
            let border_delta = RectDelta::new(
              args.left.unwrap_or(LengthValue::from_px(0)),
              args.top.unwrap_or(LengthValue::from_px(0)),
              args.right.unwrap_or(LengthValue::from_px(0)),
              args.bottom.unwrap_or(LengthValue::from_px(0)),
            );

            window.set_border_delta(border_delta);
            state.pending_sync.queue_container_to_redraw(window);

            Ok(())

View on GitHub (pinned to 5709ad0a3c)