{"record":{"id":"97c84dd5ad96ef35","repo":"Hmbown/CodeWhale","slug":"lane-was-stopped-before-inline-start-complete","errorCode":null,"errorMessage":"lane `{}` was stopped before inline start completed","messagePattern":"lane `(.+?)` was stopped before inline start completed","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/lane/src/runtime.rs","lineNumber":969,"sourceCode":"        registry: &LaneRegistry,\n        record: &mut LaneRecord,\n        spec: &LaneStartSpec,\n    ) -> Result<()> {\n        if spec.command.is_empty() {\n            bail!(\"inline runtime requires a non-empty command\");\n        }\n        let cwd = apply_worktree(record, spec)?;\n        append_log_event(\n            &record.log_path,\n            serde_json::json!({\n                \"type\": \"lane_started\",\n                \"lane_id\": record.id,\n                \"runtime\": \"inline\",\n                \"command\": spec.command,\n            }),\n        )?;\n        if !registry.mark_running_if_pending(record)? {\n            bail!(\n                \"lane `{}` was stopped before inline start completed\",\n                record.id\n            );\n        }\n\n        let mut cmd = Command::new(&spec.command[0]);\n        if spec.command.len() > 1 {\n            cmd.args(&spec.command[1..]);\n        }\n        if let Some(cwd) = cwd.as_ref() {\n            cmd.current_dir(cwd);\n        }\n        cmd.envs(spec.environment.iter().map(|(key, value)| (key, value)));\n        cmd.stdout(Stdio::piped()).stderr(Stdio::piped());\n        let mut child = match cmd.spawn() {\n            Ok(child) => child,\n            Err(err) => {\n                append_log_event(","sourceCodeStart":951,"sourceCodeEnd":987,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/lane/src/runtime.rs#L951-L987","documentation":"InlineRuntime::start applies the worktree and appends the lane_started event, then calls registry.mark_running_if_pending(record). A false return means a concurrent stop already moved the lane out of Pending, so the start aborts before spawning the child process — no orphan process is left behind, and the caller gets this explicit race signal.","triggerScenarios":"An inline lane start racing a concurrent stop on the same lane id: the stop's terminal transition commits between the lane_started log event and the Pending->Running compare-and-set.","commonSituations":"UIs letting users cancel while a lane is launching; automation that stops on a timeout fired right after start; concurrent managers operating the same lane registry.","solutions":["Refetch the lane record and treat it as stopped (it is); decide explicitly whether to start again","Await start completion before issuing stop in calling code","Coordinate concurrent lifecycle operations per lane (locks or fences)"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"match runtime.start(&registry, &mut record, &spec) {\n    Ok(()) => { /* child spawned */ }\n    Err(err) if err.to_string().contains(\"stopped before inline start completed\") => {\n        // Race lost to a concurrent stop; no child was spawned, worktree may remain per caller.\n        let fresh = registry.get(&record.id)?;\n        assert_eq!(fresh.status, LaneStatus::Stopped);\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Wait for start to finish before issuing stop on the same lane","Route all lifecycle operations for one lane through a single owner or mutex"],"tags":["rust","lane","inline","race-condition","lifecycle"],"backgroundTag":"state-transition-race","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}