atuinsh/atuin · critical

Atuin running with no parent!

Error message

Atuin running with no parent!

What it means

Shell::current needs the parent process (the shell that invoked atuin) to detect the shell name. It calls process.parent() and unwraps with expect; when the OS reports no parent PID (process is orphaned or reparented, e.g. to PID 1/init with no listed parent), the process panics with 'Atuin running with no parent!'.

Solutions

  1. Invoke atuin directly from an interactive shell rather than from a detached/orphaned process.
  2. Pass the shell explicitly (e.g. via config or the shell_name path with an explicit parent) instead of relying on detection.
  3. Relax the expect to fall back to the $SHELL environment variable when no parent exists.
  4. Avoid double-forking between the shell and the atuin invocation.

Example fix

// before
let parent = sys.process(process.parent().expect("Atuin running with no parent!"))
    .expect("Process with parent pid does not exist");

// after
let parent = process
    .parent()
    .and_then(|ppid| sys.process(ppid))
    .map(|p| p.name().to_string_lossy().to_lowercase())
    .unwrap_or_else(|| std::env::var("SHELL").unwrap_or_default());
Defensive patterns

Strategy: fallback

Validate before calling

// Rust: detect orphaned/detached context before calling Shell::current
let mut sys = sysinfo::System::new_all();
let orphaned = sysinfo::get_current_pid().ok()
    .and_then(|pid| sys.process(pid))
    .and_then(|p| p.parent())
    .is_none();
if orphaned { /* use $SHELL or explicit shell name */ }

Try / catch

// Rust
let shell = std::panic::catch_unwind(Shell::current)
    .ok()
    .or_else(|| std::env::var("SHELL").ok().map(|s| Shell::from_string(&s)))
    .unwrap_or(Shell::Unknown);

Prevention

When it happens

Trigger: Calling Shell::current from a process whose parent link is absent: double-forked daemons, orphaned processes reparented to init, or platforms where sysinfo cannot read the parent PID field.

Common situations: Running atuin commands from daemonized or detached contexts; shell wrappers that exec/double-fork before invoking atuin; minimal containers where parent metadata is unavailable.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of atuinsh/atuin@c0c717ab04 (2026-09-12). Data as JSON: /api/errors/247d83272d177b95. Report an issue: GitHub.

Appendix: source

Thrown at crates/atuin-common/src/shell.rs:48

pub enum ShellError {
    #[error("shell not supported")]
    NotSupported,

    #[error("failed to execute shell command: {0}")]
    ExecError(String),
}

impl Shell {
    #[must_use]
    pub fn current() -> Self {
        let sys = System::new_all();

        let process = sys
            .process(get_current_pid().expect("Failed to get current PID"))
            .expect("Process with current pid does not exist");

        let parent = sys
            .process(process.parent().expect("Atuin running with no parent!"))
            .expect("Process with parent pid does not exist");

        let shell = parent.name().to_string_lossy().trim().to_lowercase();
        let shell = shell.strip_prefix('-').unwrap_or(&shell);

        Self::from_string(shell)
    }

    #[must_use]
    pub fn from_env() -> Self {
        std::env::var("ATUIN_SHELL")
            .map_or(Self::Unknown, |shell| Self::from_string(&shell.trim().to_lowercase()))
    }

    #[must_use]
    pub fn config_file(&self) -> Option<std::path::PathBuf> {
        let mut path = directories::BaseDirs::new()?.home_dir().to_owned();

View on GitHub (pinned to c0c717ab04)