{"record":{"id":"a57ddba55d2a5f30","repo":"warpdotdev/warp","slug":"agent-driver-dropped-while-sending-exit","errorCode":null,"errorMessage":"Agent driver dropped while sending /exit","messagePattern":"Agent driver dropped while sending /exit","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"app/src/ai/agent_sdk/driver/harness/claude_code.rs","lineNumber":518,"sourceCode":"\n        setup_events\n            .post_timeline_event(OzRunTimelineEvent::AgentStarted)\n            .await;\n\n        Ok(command_handle)\n    }\n\n    async fn exit(&self, foreground: &ModelSpawner<AgentDriver>) -> Result<()> {\n        log::info!(\"Sending /exit to Claude Code CLI\");\n        let terminal_driver = self.terminal_driver.clone();\n        foreground\n            .spawn(move |_, ctx| {\n                terminal_driver.update(ctx, |driver, ctx| {\n                    driver.send_text_to_cli(CLAUDE_EXIT_COMMAND.to_string(), ctx);\n                });\n            })\n            .await\n            .map_err(|_| anyhow::anyhow!(\"Agent driver dropped while sending /exit\"))\n    }\n\n    async fn handle_session_update(&self, _foreground: &ModelSpawner<AgentDriver>) -> Result<()> {\n        self.handle_parent_bridge_session_update().await\n    }\n\n    async fn save_conversation(\n        &self,\n        save_point: SavePoint,\n        foreground: &ModelSpawner<AgentDriver>,\n    ) -> Result<()> {\n        if matches!(save_point, SavePoint::Periodic)\n            && !super::has_running_cli_agent(&self.terminal_driver, foreground).await\n        {\n            log::debug!(\"Will not save conversation, Claude Code not in progress\");\n            return Ok(());\n        }\n","sourceCodeStart":500,"sourceCodeEnd":536,"githubUrl":"https://github.com/warpdotdev/warp/blob/e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc/app/src/ai/agent_sdk/driver/harness/claude_code.rs#L500-L536","documentation":"Returned by the Claude Code harness exit() when ModelSpawner::spawn(...).await fails — the join/spawn channel dropped because the AgentDriver model it targets was released before the spawned update could run. It is an internal lifecycle race at shutdown, not a CLI failure: the /exit text never reached the terminal driver.","triggerScenarios":"Invoking exit() on the claude_code harness (shutdown path) while the AgentDriver foreground model is being torn down concurrently — e.g. the session was killed/crashed first, or two shutdown paths race so the model handle is dropped before the spawned closure executes.","commonSituations":"Rapid double-exit (user quits while cleanup runs); agent process already dead so the framework drops the driver mid-exit; app shutdown tearing down entities while a harness exit future is still pending. Usually cosmetic: the CLI session is ending anyway.","solutions":["Treat as non-fatal if the underlying session is already terminating — verify the claude CLI process is gone (ps aux | grep claude); kill it if it lingers.","Ensure only one component drives exit/shutdown per session to avoid racing entity teardown.","If processes leak regularly, capture logs around shutdown and report the lifecycle race against ModelSpawner/AgentDriver."],"exampleFix":"// before\nself.exit(&foreground).await?; // propagates 'dropped' error and fails shutdown\n\n// after (tolerate dropped driver at exit)\nif let Err(err) = self.exit(&foreground).await {\n    log::warn!(\"failed to send /exit (driver likely gone): {err:#}\");\n}","handlingStrategy":"try-catch","validationCode":"if foreground.is_dropped() { // or equivalent liveness check on the model handle\n    log::info!(\"agent driver already gone; skipping /exit\");\n    return Ok(());\n}","typeGuard":null,"tryCatchPattern":"if let Err(err) = self.exit(&foreground).await {\n    if err.to_string().contains(\"Agent driver dropped\") {\n        log::warn!(\"driver gone before /exit; assuming session terminated\");\n        return Ok(());\n    }\n    return Err(err);\n}","preventionTips":["Drive shutdown from a single owner per session to avoid racing entity teardown.","Treat dropped-driver errors at exit as success-equivalent (the session is ending anyway).","Verify the CLI process actually terminated with a process check, not just the exit call."],"tags":["lifecycle","claude-code","shutdown","race-condition","rust"],"backgroundTag":null,"analyzedSha":"e72fd7aacbbb2236d9b3be2aad7e7178fe94b4bc","analyzedAt":"2026-08-16T08:27:25.381Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}