herdrdev/herdr · warning · io::Error
failed to {operation} managed plugin checkout at {}; close a
Error message
failed to {operation} managed plugin checkout at {}; close any Herdr plugin panes or plugin commands using that checkout, then retry: {err} What it means
plugin_checkout_lifecycle_error (src/cli/plugin.rs:1591) wraps filesystem errors from creating/removing managed plugin checkout directories. On Windows, when the raw error is PermissionDenied, it returns a richer message telling you that a Herdr plugin pane or plugin command still holds the checkout open. Other platforms surface the raw fs error unchanged.
Source
Thrown at src/cli/plugin.rs:1591
return Ok(());
};
let path = PathBuf::from(path);
if !path.exists() {
return Ok(());
}
if !is_expected_managed_path(plugin, &path) {
return Err(std::io::Error::other(format!(
"refusing to delete unmanaged plugin path: {}",
path.display()
)));
}
std::fs::remove_dir_all(&path)
.map_err(|err| plugin_checkout_lifecycle_error("remove", &path, err))
}
fn plugin_checkout_lifecycle_error(operation: &str, path: &Path, err: io::Error) -> io::Error {
if cfg!(windows) && err.kind() == io::ErrorKind::PermissionDenied {
return io::Error::new(
err.kind(),
format!(
"failed to {operation} managed plugin checkout at {}; close any Herdr plugin panes or plugin commands using that checkout, then retry: {err}",
path.display()
),
);
}
err
}
fn is_expected_managed_path(plugin: &InstalledPluginInfo, path: &Path) -> bool {
let Ok(path) = path.canonicalize() else {
return false;
};
let expected = crate::plugin_paths::managed_checkout_path(&plugin.plugin_id);
let Ok(expected) = expected.canonicalize() else {
return false;
};View on GitHub (pinned to f457cff4f2)
Solutions
- Close all Herdr plugin panes and any terminals/editors using that checkout, then retry the operation
- Stop builds or dev servers running inside the plugin checkout
- Retry after a short delay if antivirus/indexing was transiently locking files
- As a last resort, restart Herdr so all child processes release handles, then retry
Defensive patterns
Strategy: retry
Try / catch
for attempt in 0..3 {
match remove_checkout(&path) {
Ok(()) => break,
Err(e) if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied => {
std::thread::sleep(std::time::Duration::from_secs(1 << attempt));
}
Err(e) => return Err(e),
}
} Prevention
- Close plugin panes and stop builds before uninstalling or updating plugins
- Treat Windows PermissionDenied on directory removal as transient and retry after closing handles
When it happens
Trigger: Calling the checkout remove/create path (e.g. std::fs::remove_dir_all at src/cli/plugin.rs:1588-1590, or the corresponding create) on Windows while a process still has files in the checkout open — a running plugin pane, a plugin command shell, an editor, or antivirus scanning the tree.
Common situations: Uninstalling/updating a plugin while its pane is open in another Herdr tab; a build process (cargo/node) still running in the checkout; Windows Defender or a file indexer locking files; the directory being open in Explorer.
Related errors
- failed to create unique scrollback temp file
- build command must not be empty
- direct terminal attach is not supported on Windows yet
- failed to create private herdr ssh config directory
- failed to create private herdr ssh config directory
AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28).
Data as JSON: /api/errors/f5b15112b1073a20.
Report an issue: GitHub.