zellij-org/zellij · error · anyhow::Error

Unspecified exit reason

Error message

Unspecified exit reason

What it means

Thrown by TryFrom<ProtoExitReason> for ExitReason when the protobuf enum value is Unspecified. In proto3, Unspecified (=0) is the default value, so it typically means the sender never set the exit_reason field. The converter deliberately refuses to guess an exit reason instead of silently mapping it to Normal.

Source

Thrown at zellij-utils/src/ipc/protobuf_conversion.rs:3369

            ExitReason::CustomExitStatus(_status) => ProtoExitReason::CustomExitStatus,
        }
    }
}

impl TryFrom<ProtoExitReason> for ExitReason {
    type Error = anyhow::Error;
    fn try_from(reason: ProtoExitReason) -> Result<Self> {
        match reason {
            ProtoExitReason::Normal => Ok(ExitReason::Normal),
            ProtoExitReason::NormalDetached => Ok(ExitReason::NormalDetached),
            ProtoExitReason::ForceDetached => Ok(ExitReason::ForceDetached),
            ProtoExitReason::CannotAttach => Ok(ExitReason::CannotAttach),
            ProtoExitReason::Disconnect => Ok(ExitReason::Disconnect),
            ProtoExitReason::WebClientsForbidden => Ok(ExitReason::WebClientsForbidden),
            ProtoExitReason::KickedByHost => Ok(ExitReason::KickedByHost),
            ProtoExitReason::Error => Ok(ExitReason::Error("Protobuf error".to_string())),
            ProtoExitReason::CustomExitStatus => Ok(ExitReason::CustomExitStatus(0)),
            ProtoExitReason::Unspecified => Err(anyhow!("Unspecified exit reason")),
        }
    }
}

impl From<HostTerminalThemeMode> for ProtoHostTerminalThemeIndication {
    fn from(mode: HostTerminalThemeMode) -> Self {
        match mode {
            HostTerminalThemeMode::Dark => ProtoHostTerminalThemeIndication::Dark,
            HostTerminalThemeMode::Light => ProtoHostTerminalThemeIndication::Light,
        }
    }
}

impl From<ProtoHostTerminalThemeIndication> for HostTerminalThemeMode {
    fn from(mode: ProtoHostTerminalThemeIndication) -> Self {
        match mode {
            ProtoHostTerminalThemeIndication::Dark => HostTerminalThemeMode::Dark,
            ProtoHostTerminalThemeIndication::Light => HostTerminalThemeMode::Light,

View on GitHub (pinned to 98a0837077)

Solutions

  1. Update both client and server to matching zellij versions so exit_reason is always populated
  2. Fix the sender to always set a concrete exit_reason value (never rely on proto3 defaults)
  3. If you control the conversion, map Unspecified to a defined fallback such as ExitReason::Normal instead of erroring

Example fix

// before
ProtoExitReason::Unspecified => Err(anyhow!("Unspecified exit reason")),

// after (defensive default)
ProtoExitReason::Unspecified => Ok(ExitReason::Normal),
Defensive patterns

Strategy: validation

Validate before calling

use zellij_utils::interprocess::contract::ExitReason as ProtoExitReason; // adjust path to generated contract
fn exit_reason_is_set(reason: ProtoExitReason) -> bool {
    !matches!(reason, ProtoExitReason::Unspecified)
}

Type guard

fn is_concrete_exit_reason(r: ProtoExitReason) -> bool {
    !matches!(r, ProtoExitReason::Unspecified)
}

Try / catch

match ProtoExitReason::try_from(raw) {
    Ok(reason) => use_reason(reason),
    Err(e) if e.to_string().contains("Unspecified exit reason") => {
        log::warn!("exit reason unset by peer; treating as normal");
        use_reason(ExitReason::Normal);
    },
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Decoding an ExitReason protobuf message whose enum field was never populated (proto3 zero-value), e.g. a client/server built from a different contract version, or a hand-crafted/malformed IPC payload passed to the conversion in zellij-utils/src/ipc/protobuf_conversion.rs:3369.

Common situations: Version skew between zellij client and server where one side stops setting exit_reason; test fixtures or custom clients that construct the proto message without setting the field.

Related errors


AI-assisted analysis of zellij-org/zellij@98a0837077 (2026-08-16). Data as JSON: /api/errors/acd3412fd9b83783. Report an issue: GitHub.