From 392a10107863e84212abf5c14abf441fd5c48656 Mon Sep 17 00:00:00 2001 From: Zane <75694352+zanesq@users.noreply.github.com> Date: Tue, 6 May 2025 15:23:00 -0700 Subject: [PATCH] Fix resume session with an invalid path crash (#2453) --- crates/goose-cli/src/session/builder.rs | 12 +++++- crates/goose/src/session/storage.rs | 49 ++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/crates/goose-cli/src/session/builder.rs b/crates/goose-cli/src/session/builder.rs index 9c9092b040..38ce3c7f26 100644 --- a/crates/goose-cli/src/session/builder.rs +++ b/crates/goose-cli/src/session/builder.rs @@ -103,7 +103,17 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> Session { .interact().expect("Failed to get user input"); if change_workdir { - std::env::set_current_dir(metadata.working_dir).unwrap(); + if !metadata.working_dir.exists() { + output::render_error(&format!( + "Cannot switch to original working directory - {} no longer exists", + style(metadata.working_dir.display()).cyan() + )); + } else if let Err(e) = std::env::set_current_dir(&metadata.working_dir) { + output::render_error(&format!( + "Failed to switch to original working directory: {}", + e + )); + } } } } diff --git a/crates/goose/src/session/storage.rs b/crates/goose/src/session/storage.rs index 554ab02f9b..3830966702 100644 --- a/crates/goose/src/session/storage.rs +++ b/crates/goose/src/session/storage.rs @@ -62,6 +62,12 @@ impl<'de> Deserialize<'de> for SessionMetadata { let helper = Helper::deserialize(deserializer)?; + // Get working dir, falling back to home if not specified or if specified dir doesn't exist + let working_dir = helper + .working_dir + .filter(|path| path.exists()) + .unwrap_or_else(get_home_dir); + Ok(SessionMetadata { description: helper.description, message_count: helper.message_count, @@ -71,13 +77,20 @@ impl<'de> Deserialize<'de> for SessionMetadata { accumulated_total_tokens: helper.accumulated_total_tokens, accumulated_input_tokens: helper.accumulated_input_tokens, accumulated_output_tokens: helper.accumulated_output_tokens, - working_dir: helper.working_dir.unwrap_or_else(get_home_dir), + working_dir, }) } } impl SessionMetadata { pub fn new(working_dir: PathBuf) -> Self { + // If working_dir doesn't exist, fall back to home directory + let working_dir = if !working_dir.exists() { + get_home_dir() + } else { + working_dir + }; + Self { working_dir, description: String::new(), @@ -563,4 +576,38 @@ mod tests { Ok(()) } + + #[test] + fn test_invalid_working_dir() -> Result<()> { + let dir = tempdir()?; + let file_path = dir.path().join("test.jsonl"); + + // Create metadata with non-existent directory + let invalid_dir = PathBuf::from("/path/that/does/not/exist"); + let metadata = SessionMetadata::new(invalid_dir.clone()); + + // Should fall back to home directory + assert_ne!(metadata.working_dir, invalid_dir); + assert_eq!(metadata.working_dir, get_home_dir()); + + // Test deserialization of invalid directory + let messages = vec![Message::user().with_text("test")]; + save_messages_with_metadata(&file_path, &metadata, &messages)?; + + // Modify the file to include invalid directory + let contents = fs::read_to_string(&file_path)?; + let mut lines: Vec = contents.lines().map(String::from).collect(); + lines[0] = lines[0].replace( + &get_home_dir().to_string_lossy().into_owned(), + &invalid_dir.to_string_lossy().into_owned(), + ); + fs::write(&file_path, lines.join("\n"))?; + + // Read back - should fall back to home dir + let read_metadata = read_metadata(&file_path)?; + assert_ne!(read_metadata.working_dir, invalid_dir); + assert_eq!(read_metadata.working_dir, get_home_dir()); + + Ok(()) + } }