sxyazi/yazi · error

Some files cannot be selected, due to path nesting conflict.

Error message

Some files cannot be selected, due to path nesting conflict.

What it means

When escaping visual mode with selection, yazi tries to add all visual-mode files to the tab's persistent selection via `add_many`. The selection set rejects entries whose paths are nested inside another selected entry (a file inside an already-selected directory would be selected twice). If some entries could not be added, the actor warns and bails instead of silently producing an inconsistent selection.

Solutions

  1. Flatten the selection before exiting visual mode: keep only top-most entries (drop children of already-selected directories)
  2. Handle the bail in your keymap/plugin and notify the user to review the selection
  3. Modify the selection incrementally with `add` checks or pre-filter nested paths before calling the actor

Example fix

// before
let files = dir_and_children; // contains parent + nested children
act!(mgr:escape_visual, cx, (true, files))?;
// after
let files: Vec<_> = dir_and_children.into_iter()
    .filter(|f| !dir_and_children.iter().any(|o| o.url != f.url && f.url.starts_with(&o.url)))
    .collect();
act!(mgr:escape_visual, cx, (true, files))?;
Defensive patterns

Strategy: try-catch

Validate before calling

fn has_nested_conflict(files: &[File]) -> bool {
    files.iter().any(|f| files.iter().any(|o| {
        o.url != f.url && f.url.starts_with(&o.url)
    }))
}

Type guard

fn is_safe_selection(files: &[File]) -> bool {
    !has_nested_conflict(files)
}

Try / catch

match escape_visual(cx, files).await {
    Err(e) if e.to_string().contains("path nesting conflict") => {
        notify::warn("Selection contains a directory and its children; deselect one");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Pressing the escape/visual-exit key that runs this actor while the visual selection contains both a directory and files (or subdirectories) nested inside it, so `Selected::add_many` returns a count smaller than `files.len()`.

Common situations: User selects a parent directory, then moves the cursor into it and selects children in visual mode, then exits visual mode expecting everything to stay selected; bulk-selection plugins that gather recursive file lists including ancestors.

Related errors


AI-assisted analysis of sxyazi/yazi@a541adde4e (2026-09-02). Data as JSON: /api/errors/a6ab13e9b38444af. Report an issue: GitHub.

Appendix: source

Thrown at yazi-actor/src/mgr/escape.rs:85

	fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
		let tab = cx.tab_mut();

		let select = tab.mode.is_select();
		let Some(indices) = tab.mode.take_visual(tab.current.cursor, tab.current.entries.len()) else {
			succ!(false)
		};

		render!();
		let files: Vec<_> = indices.into_iter().filter_map(|i| tab.current.entries.get(i)).collect();

		if !select {
			tab.selected.remove_many(files);
		} else if files.len() != tab.selected.add_many(files) {
			NotifyProxy::push_warn(
				"Escape visual mode",
				"Some files cannot be selected, due to path nesting conflict.",
			);
			bail!("Some files cannot be selected, due to path nesting conflict.");
		}

		succ!(true)
	}
}

// --- Filter
pub struct EscapeFilter;

impl Actor for EscapeFilter {
	type Form = VoidForm;

	const NAME: &str = "escape_filter";

	fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
		if cx.current_mut().entries.filter().is_none() {
			succ!(false);
		}

View on GitHub (pinned to a541adde4e)