astrid-runtime/astrid · error
REPL history path is not a regular file: {}
Error message
REPL history path is not a regular file: {} What it means
Before rustyline loads the REPL history file, `Repl::new` verifies via symlink_metadata that the path is a real regular file — not a symlink and not a special entry (FIFO, socket, directory, device). This protects the history file from being redirected through a user-controlled symlink or special file, which could leak or corrupt data.
Source
Thrown at crates/astrid-cli/src/repl.rs:116
///
/// Loads command history from the operator-only `log/cli/history` path
/// (creating the private file if it does not yet exist) and configures tab
/// completion for slash commands. History is never stored in a principal
/// home or capsule-visible namespace.
pub(crate) fn new() -> anyhow::Result<Self> {
let home = astrid_core::dirs::AstridHome::resolve()?;
home.ensure()?;
let history_dir = home.log_dir().join("cli");
astrid_core::platform_fs::ensure_private_directory(&history_dir)?;
let history_path = history_dir.join("history");
migrate_legacy_history(&history_path, &home.root().join("history"))?;
// Ensure the history file exists and is a regular, private, no-follow
// file so rustyline cannot be redirected through a user-controlled
// symlink or special entry.
match std::fs::symlink_metadata(&history_path) {
Ok(metadata) if metadata.file_type().is_symlink() || !metadata.is_file() => {
anyhow::bail!(
"REPL history path is not a regular file: {}",
history_path.display()
);
},
Ok(_) => {
astrid_core::platform_fs::verify_no_redirects(&history_path)?;
astrid_core::platform_fs::restrict_private_file(&history_path)?;
},
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
astrid_core::platform_fs::atomic_write_private_file(&history_path, b"")?;
},
Err(error) => return Err(error.into()),
}
let config = Config::builder()
.history_ignore_dups(true)?
.completion_type(CompletionType::List)
.edit_mode(EditMode::Emacs)View on GitHub (pinned to affd8760f4)
Solutions
- Remove the offending entry at the history path (`rm` the symlink/special file).
- Create a fresh regular file in its place (the REPL will append to it) or let rustyline create it.
- Run the REPL with ASTRID home/history pointing at a clean location.
- Check for dotfile managers or restore scripts that symlink this path and adjust them to copy instead.
Example fix
// before ln -s /mnt/share/bash_history ~/.astrid/history # symlink -> error // after rm ~/.astrid/history && touch ~/.astrid/history # regular file
Defensive patterns
Strategy: validation
Validate before calling
let history_path = default_history_path();
match std::fs::symlink_metadata(&history_path) {
Ok(m) if m.is_file() => Ok(()),
Ok(_) | Err(_) => {
let _ = std::fs::remove_file(&history_path); // drop symlink/special entry
std::fs::File::create(&history_path).map(|_| ())
}
} Type guard
fn is_regular_no_follow(p: &std::path::Path) -> bool {
std::fs::symlink_metadata(p)
.map(|m| m.is_file())
.unwrap_or(false)
} Try / catch
match Repl::new(...) {
Ok(repl) => repl,
Err(e) if e.to_string().contains("not a regular file") => {
eprintln!("History path is a symlink/special file; recreating it");
let _ = std::fs::remove_file(&history_path);
Repl::new(...)?
}
Err(e) => return Err(e),
} Prevention
- Never symlink the REPL history path; dotfile managers should copy it.
- Recreate the history file if a restore/backup tool replaced it.
- Point the history path at a user-private directory (0700) so others can't plant entries.
- Call verify_no_redirects before trusting any user-supplied path.
When it happens
Trigger: Starting the REPL when the history file path exists as a symlink, a directory, a FIFO/socket/device, or otherwise fails the is_file() check on its symlink metadata.
Common situations: A backup/restore tool replaced the history file with a symlink; a dotfile manager (e.g. stow, symlink farms) linked the history path; the path was created as a directory by mistake; a malicious setup planted a special file at the path.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- leftover capsule authority receipt is not a regular file: {}
- legacy capsule authority root is not a regular directory: {}
- legacy capsule authority root contains a non-regular entry:
- legacy capsule authority root is not a regular directory: {}
- directory symlink {} not allowed in capsule source tree (ref
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/3c83169ffc8b5fa2.
Report an issue: GitHub.