Zackriya-Solutions/meetily · critical · anyhow::Error

Sidecar closed stdout (process may have crashed)

Error message

Sidecar closed stdout (process may have crashed)

What it means

read_response got EOF on the sidecar's stdout: read_line returned zero bytes, meaning the llama-helper process has exited. The helper crashed or was killed - fatal model-load error inside the helper, a panic, the OOM killer, or an external kill. stderr is inherited (Stdio::inherit), so the helper's own dying message usually appears in the app log just before this error.

Source

Thrown at frontend/src-tauri/src/summary/summary_engine/sidecar.rs:406

            }
        }
    }

    /// Read a single line response from stdout
    async fn read_response(&self) -> Result<String> {
        let mut stdout_lock = self.stdout_reader.lock().await;
        let reader = stdout_lock
            .as_mut()
            .ok_or_else(|| anyhow!("Sidecar not running"))?;

        let mut line = String::new();
        reader
            .read_line(&mut line)
            .await
            .context("Failed to read response from stdout")?;

        if line.is_empty() {
            return Err(anyhow!("Sidecar closed stdout (process may have crashed)"));
        }

        Ok(line.trim().to_string())
    }

    /// Send ping to keep sidecar alive
    async fn send_ping(&self) -> Result<()> {
        let request = serde_json::json!({"type": "ping"}).to_string();
        let timeout = Duration::from_secs(5);

        // Note: We don't use send_request here to avoid incrementing active_request_count
        // for internal health checks, as that would prevent graceful shutdown
        
        // Write request
        {
            let mut stdin_lock = self.stdin_writer.lock().await;
            if let Some(stdin) = stdin_lock.as_mut() {
                stdin.write_all(request.as_bytes()).await?;

View on GitHub (pinned to 0281737d87)

Solutions

  1. Check the app log for the helper's stderr immediately before the EOF - it names the real cause (model load failure, allocation failure, panic)
  2. If memory was the cause, free RAM or use a smaller summary model
  3. Verify the model path passed to ensure_running still exists and is a valid GGUF
  4. Rebuild llama-helper from the same source revision as the app so the JSON-line protocol matches
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: the model must be readable and the process alive before sending
if !model_path.is_file() { return Err(anyhow!("model file missing: {:?}", model_path)); }
if !sidecar.is_healthy() { sidecar.ensure_running(model_path.clone()).await?; }

Try / catch

match sidecar.send_request(req, timeout).await {
    Err(e) if e.to_string().contains("closed stdout") => {
        log::error!("sidecar crashed; stderr (inherited) shows the cause just above");
        sidecar.ensure_running(model_path.clone()).await?; // respawn
        match sidecar.send_request(req, timeout).await {
            Err(e2) if e2.to_string().contains("closed stdout") =>
                Err(anyhow!("sidecar crashes immediately; check RAM and rebuild llama-helper")),
            other => other,
        }
    }
    other => other,
}?

Prevention

When it happens

Trigger: llama-helper dies while handling a request: out-of-memory loading or running a large GGUF, a panic in the helper, an invalid/unreadable model path passed at spawn, or an external signal (OOM killer, task manager, container cgroup kill).

Common situations: Large model on machines with tight RAM (OOM kill), model file deleted or moved after the sidecar loaded it, helper binary built from a different source revision whose stdin/stdout protocol no longer matches the app.

Related errors


AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16). Data as JSON: /api/errors/680f80d393c8c8c0. Report an issue: GitHub.