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

Sidecar not running

Error message

Sidecar not running

What it means

send_request writes a JSON line to the sidecar's stdin, but stdin_writer is None when no sidecar process is attached. That happens when send_request runs before ensure_running()/spawn() completes, or after shutdown() - including the automatic shutdown performed by the timeout path in send_request itself.

Source

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

        // Start background tasks
        self.start_health_check_loop();
        self.start_idle_check_loop();

        Ok(())
    }

    /// Send a request to the sidecar and wait for response
    pub async fn send_request(&self, request_json: String, timeout: Duration) -> Result<String> {
        // Track active request
        let _guard = RequestGuard::new(self.active_request_count.clone());

        // Write request to stdin
        {
            let mut stdin_lock = self.stdin_writer.lock().await;
            let stdin = stdin_lock
                .as_mut()
                .ok_or_else(|| anyhow!("Sidecar not running"))?;

            stdin
                .write_all(request_json.as_bytes())
                .await
                .context("Failed to write request to stdin")?;
            stdin
                .write_all(b"\n")
                .await
                .context("Failed to write newline")?;
            stdin.flush().await.context("Failed to flush stdin")?;
        }

        // Read response from stdout with timeout
        match tokio::time::timeout(timeout, self.read_response()).await {
            Ok(Ok(response)) => {
                self.update_activity().await;
                Ok(response)
            }

View on GitHub (pinned to 0281737d87)

Solutions

  1. Always pair requests with lifecycle: await ensure_running(&model_path) before send_request - it spawns when unhealthy
  2. After a timeout error, call ensure_running again before retrying (the sidecar was killed by design)
  3. Serialize requests via the existing active_request_count/RequestGuard so shutdown cannot race an in-flight write

Example fix

// before - send may hit a shut-down sidecar
let resp = sidecar.send_request(request_json, timeout).await?;

// after - ensure the process is alive (spawns or respawns as needed)
sidecar.ensure_running(model_path.clone()).await?;
let resp = sidecar.send_request(request_json, timeout).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Lifecycle gate before every request
if !sidecar.is_healthy() {
    sidecar.ensure_running(model_path.clone()).await?;
}
let resp = sidecar.send_request(request_json, timeout).await?;

Type guard

// Rust: narrow on the live handle rather than a boolean
pub async fn live_stdin(sidecar: &LlamaHelper) -> bool {
    sidecar.stdin_writer.read().await.is_some()
}

Try / catch

match sidecar.send_request(req, timeout).await {
    Err(e) if e.to_string().contains("Sidecar not running") => {
        sidecar.ensure_running(model_path.clone()).await?; // respawn
        sidecar.send_request(req, timeout).await          // one retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling send_request without a prior successful ensure_running(model_path); sending a follow-up request after a previous request timed out (the timeout branch calls shutdown() and clears handles); a race where one task shuts the sidecar down while another is mid-write.

Common situations: Retry logic that resubmits after a timeout without respawning; concurrent summarization tasks where one triggers idle-shutdown while another writes; first request after the sidecar's LLAMA_IDLE_TIMEOUT reaped the process.

Related errors


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