From 321bf70300f44e1035447951cffb0107f4524058 Mon Sep 17 00:00:00 2001 From: "posthog[bot]" <206114724+posthog[bot]@users.noreply.github.com> Date: Sat, 19 Sep 2026 16:29:41 +0000 Subject: [PATCH] fix: treat missing git objects as benign teardown race in daemon Widen is_missing_working_dir_error to match the missing-object exit-128 stderr strings ("not a tree object", "bad object", "unknown revision"). A snapshot side effect passes a tree oid to `ls-tree` in committed_file_snapshot_between_commits. When a temp repo is torn down mid-operation its directory can survive while its objects are gone, so git answers "fatal: not a tree object". The old classifier matched only "No such file or directory" and "not a git repository", so this shape slipped past the guard, hit tracing::error!, and SentryLayer turned it into a fresh error-tracking issue for a benign race. The side effect is skipped either way, so the daemon now skips it quietly at debug level. Generated-By: PostHog Desktop Task-Id: c4d492f8-b2ae-45e7-ba2b-52ef0fe09bdd --- src/daemon.rs | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/src/daemon.rs b/src/daemon.rs index 6adb325..e192c0b 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -331,17 +331,21 @@ fn is_trace_payload(payload: &Value) -> bool { payload.get("event").and_then(Value::as_str).is_some() } -/// Returns true when the error indicates the git working directory vanished +/// Returns true when the error indicates a git repository vanished /// mid-operation, typically when a temp/test repo is cleaned up while an async /// side effect is still in flight. These are benign races, so the daemon skips /// the side effect quietly rather than reporting a generic "command side effect /// failed" exception. /// -/// The same race surfaces in three shapes: +/// The same race surfaces in several shapes: /// - git cannot enter the directory: exit 128, stderr "No such file or /// directory". /// - git finds no repository at or above the gone directory: exit 128, stderr /// "not a git repository". +/// - the directory still exists but its objects are gone, so an oid that was +/// valid at read time no longer resolves: exit 128, stderr "not a tree +/// object", "bad object", or "unknown revision". This happens when a snapshot +/// side effect passes a tree oid to `ls-tree` after the object was deleted. /// - on Windows the directory read fails before git runs: an `IoError` with a /// not-found kind ("The system cannot find the path specified"). fn is_missing_working_dir_error(error: &AutterError) -> bool { @@ -351,7 +355,11 @@ fn is_missing_working_dir_error(error: &AutterError) -> bool { stderr, .. } => { - stderr.contains("No such file or directory") || stderr.contains("not a git repository") + stderr.contains("No such file or directory") + || stderr.contains("not a git repository") + || stderr.contains("not a tree object") + || stderr.contains("bad object") + || stderr.contains("unknown revision") } AutterError::IoError(io_error) => io_error.kind() == std::io::ErrorKind::NotFound, _ => false, @@ -9320,6 +9328,25 @@ mod tests { assert!(is_missing_working_dir_error(&error)); } + #[test] + fn missing_working_dir_error_detects_missing_object() { + // The directory survives but its objects are gone: an oid that was valid + // when read no longer resolves. `ls-tree ` reports "not a tree + // object", and oid lookups report "bad object" or "unknown revision". + for stderr in [ + "fatal: not a tree object", + "fatal: bad object 0123456789abcdef0123456789abcdef01234567", + "fatal: ambiguous argument 'abc123': unknown revision or path not in the working tree.", + ] { + let error = AutterError::GitCliError { + code: Some(128), + stderr: stderr.to_string(), + args: vec!["ls-tree".to_string(), "-r".to_string()], + }; + assert!(is_missing_working_dir_error(&error), "stderr: {stderr}"); + } + } + #[test] fn missing_working_dir_error_ignores_other_git_failures() { // Exit 128 but a different cause is a real error, not a vanished dir.