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
- Always pair requests with lifecycle: await ensure_running(&model_path) before send_request - it spawns when unhealthy
- After a timeout error, call ensure_running again before retrying (the sidecar was killed by design)
- 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
- Never call send_request without a preceding ensure_running in the same task
- Treat a timeout error as implicitly destroying the sidecar: the next call must respawn
- Serialize requests so shutdown (manual or idle) cannot race an in-flight write
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
- Generation failed: {}
- Sidecar error: {}
- Unknown model: {}
- Unknown template: {}
- Failed to determine project root
AI-assisted analysis of Zackriya-Solutions/meetily@0281737d87 (2026-08-16).
Data as JSON: /api/errors/e67bb4e85163b7fd.
Report an issue: GitHub.