moghtech/komodo · info · anyhow::Error

pty exited

Error message

pty exited

What it means

send_terminal_exited sends a TerminalMessage carrying an Err(anyhow!("pty exited")) to notify the remote side that the PTY backing the terminal channel has terminated. The message text is deliberately a sentinel value used to propagate the exit state through the normal message channel rather than a fault of the send itself.

Solutions

  1. Treat the received error as a normal end-of-session signal and close the terminal UI for that channel
  2. Inspect the pty exit code at the source if you need to report why it exited
  3. Do not attempt to keep sending terminal input to that channel UUID; request a new terminal if more interaction is needed
  4. Render a friendly 'session ended' message to the end user instead of surfacing the raw error

Example fix

// before
TerminalMessage::Err(e) => return Err(e),
// after
TerminalMessage::Err(e) if e.to_string() == "pty exited" => {
  ui.close_terminal(channel); // normal session end
}
Defensive patterns

Strategy: try-catch

Try / catch

match msg {
  TerminalMessage::Err(e) if e.to_string() == "pty exited" => {
    ui.show_session_ended(channel); // expected end-of-session
  }
  TerminalMessage::Err(e) => return Err(e),
  TerminalMessage::Message(m) => ui.write(channel, &m),
  _ => {}
}

Prevention

When it happens

Trigger: The library calls this (public) method when the pty process spawned for a terminal channel exits — the consumer receives a TerminalMessage whose payload is the error "pty exited" for the given channel UUID.

Common situations: User types `exit` in an attached shell; the shell crashes; the container/process behind the pty is killed; ssh exec channel terminates after command completion.


AI-assisted analysis of moghtech/komodo@780ac68b99 (2026-09-08). Data as JSON: /api/errors/44e60cffa74e59ae. Report an issue: GitHub.

Appendix: source

Thrown at lib/transport/src/channel.rs:137

      .await
  }

  pub async fn send_terminal(
    &self,
    channel: Uuid,
    data: anyhow::Result<Vec<u8>>,
  ) -> anyhow::Result<()> {
    self.send_message(TerminalMessage::new(channel, data)).await
  }

  pub async fn send_terminal_exited(
    &self,
    channel: Uuid,
  ) -> anyhow::Result<()> {
    self
      .send_message(TerminalMessage::new(
        channel,
        Err(anyhow!("pty exited")),
      ))
      .await
  }
}

#[derive(Debug)]
pub struct Receiver<T> {
  receiver: mpsc::Receiver<T>,
  cancel: Option<CancellationToken>,
}

impl<T: Send> Receiver<T> {
  pub fn set_cancel(&mut self, cancel: CancellationToken) {
    self.cancel = Some(cancel);
  }

  pub fn poll_recv(
    &mut self,

View on GitHub (pinned to 780ac68b99)