diff --git a/crates/bsk-cli/src/cli/record.rs b/crates/bsk-cli/src/cli/record.rs index b5a8460..350ae52 100644 --- a/crates/bsk-cli/src/cli/record.rs +++ b/crates/bsk-cli/src/cli/record.rs @@ -190,7 +190,8 @@ fn dispatch_stop(args: RecordStopArgs, format: Format) -> Result<(), CliError> { Method::ToolRecordStop, Some(params), TOOL_IPC_TIMEOUT, - )?; + ) + .map_err(|err| forget_stale_recording(err, &session_id))?; let run_result: Result<(), CliError> = (|| { let exported = export_with_recovery(&args.output, &result.trace)?; @@ -215,6 +216,29 @@ fn dispatch_stop(args: RecordStopArgs, format: Format) -> Result<(), CliError> { render_finish(&trace, &args.output, &exported, format) } +/// A `not_found` stop means the session or its recording is gone, so the +/// saved pointer can never be stopped; drop it so `record start` works again. +fn forget_stale_recording(err: CliError, session_id: &str) -> CliError { + let CliError::Rpc { + code: ErrorCode::NotFound, + message, + data, + source, + } = err + else { + return err; + }; + record_state::clear(); + CliError::Rpc { + code: ErrorCode::NotFound, + message: format!( + "{message}; cleared the stale recording state for session {session_id}, so `bsk record start` can run again" + ), + data, + source, + } +} + /// When `record start` omitted `--url` and the default example.com page /// never loaded, rewrite the RPC error so the CLI hint points at `--url`. fn annotate_default_start_page_error(err: CliError, user_url: Option<&str>) -> CliError { @@ -408,6 +432,45 @@ mod tests { }); } + #[test] + fn stop_of_a_vanished_session_unblocks_record_start() { + with_temp_home(|| { + record_state::write("megi").unwrap(); + let err = CliError::from_rpc(bsk_protocol::RpcError { + code: ErrorCode::NotFound, + message: "session not registered or already stopped".into(), + data: None, + }); + + let err = forget_stale_recording(err, "megi"); + + assert_eq!(err.code(), Some(ErrorCode::NotFound)); + assert!( + err.to_string() + .contains("cleared the stale recording state") + ); + assert!(record_state::read().is_err()); + prepare_record_start(Path::new("trace")).unwrap(); + }); + } + + #[test] + fn failed_flush_keeps_the_recording_state_for_a_retry() { + with_temp_home(|| { + record_state::write("oabu").unwrap(); + let err = CliError::from_rpc(bsk_protocol::RpcError { + code: ErrorCode::ProtocolError, + message: "failed to flush recorded steps for session oabu".into(), + data: None, + }); + + let err = forget_stale_recording(err, "oabu"); + + assert_eq!(err.code(), Some(ErrorCode::ProtocolError)); + assert_eq!(record_state::read().unwrap().session_id, "oabu"); + }); + } + #[test] fn default_output_is_trace_dir() { let args = RecordStopArgs {