agent: Show title generation error in tooltip (#60982)

Closes #57117

<img width="305" height="84" alt="image"
src="https://github.com/user-attachments/assets/5626040c-5c3f-4f3f-8931-31331406015e"
/>

Release Notes:

- agent: Show actual error message when title generation fails

---------

Co-authored-by: Martin Ye <martin@zed.dev>
This commit is contained in:
Bennet Bo FennerandMartin Ye authored and GitHub committed 2026-07-14 15:48:02 +00:00
1 parent b4018f4c7e
commit 75aeecfbf3
3 files changed
+41 -19

No files matched your search

+7
View File
@@ -3842,6 +3842,11 @@ async fn test_title_generation_failure_allows_retry(cx: &mut TestAppContext) {
thread.read_with(cx, |thread, _| {
assert_eq!(thread.title(), None);
assert!(thread.has_failed_title_generation());
assert!(
thread
.title_generation_error()
.is_some_and(|error| error.contains("Internal server error"))
);
assert!(!thread.is_generating_title());
});
@@ -3852,6 +3857,7 @@ async fn test_title_generation_failure_allows_retry(cx: &mut TestAppContext) {
thread.read_with(cx, |thread, _| {
assert!(!thread.has_failed_title_generation());
assert_eq!(thread.title_generation_error(), None);
assert!(thread.is_generating_title());
});
@@ -3862,6 +3868,7 @@ async fn test_title_generation_failure_allows_retry(cx: &mut TestAppContext) {
thread.read_with(cx, |thread, _| {
assert_eq!(thread.title(), Some("Retried title".into()));
assert!(!thread.has_failed_title_generation());
assert_eq!(thread.title_generation_error(), None);
assert!(!thread.is_generating_title());
});
}
+23 -15
View File
@@ -1219,7 +1219,7 @@ pub struct Thread {
updated_at: DateTime<Utc>,
title: Option<SharedString>,
pending_title_generation: Option<Task<()>>,
title_generation_failed: bool,
title_generation_error: Option<SharedString>,
pending_summary_generation: Option<Shared<Task<Option<SharedString>>>>,
summary: Option<SharedString>,
messages: Vec<Arc<Message>>,
@@ -1369,7 +1369,7 @@ impl Thread {
updated_at: Utc::now(),
title: None,
pending_title_generation: None,
title_generation_failed: false,
title_generation_error: None,
pending_summary_generation: None,
summary: None,
messages: Vec::new(),
@@ -1748,7 +1748,7 @@ impl Thread {
Some(db_thread.title.clone())
},
pending_title_generation: None,
title_generation_failed: false,
title_generation_error: None,
pending_summary_generation: None,
summary: db_thread.detailed_summary,
messages: db_thread.messages,
@@ -3751,7 +3751,11 @@ impl Thread {
}
pub fn has_failed_title_generation(&self) -> bool {
self.title_generation_failed
self.title_generation_error.is_some()
}
pub fn title_generation_error(&self) -> Option<SharedString> {
self.title_generation_error.clone()
}
pub fn can_generate_title(&self) -> bool {
@@ -3854,7 +3858,7 @@ impl Thread {
on_generated_title: Option<Box<dyn FnOnce(SharedString, &mut Context<Self>)>>,
cx: &mut Context<Self>,
) {
self.title_generation_failed = false;
self.title_generation_error = None;
log::debug!("Generating title with model: {:?}", model.name());
let temperature = AgentSettings::temperature_for_model(&model, cx);
@@ -3865,22 +3869,26 @@ impl Thread {
.await
.context("failed to generate thread title")
.map(SharedString::from)
.log_err()
});
self.pending_title_generation = Some(cx.spawn(async move |this, cx| {
let title = title_generation.await;
_ = this.update(cx, |this, cx| {
this.pending_title_generation = None;
if let Some(title) = title {
this.set_title(title.clone(), cx);
if let Some(on_generated_title) = on_generated_title {
on_generated_title(title, cx);
match title {
Ok(title) => {
this.set_title(title.clone(), cx);
if let Some(on_generated_title) = on_generated_title {
on_generated_title(title, cx);
}
}
Err(error) => {
let error = format!("{error:#}");
log::error!("{error}");
this.title_generation_error = Some(error.into());
cx.emit(TitleUpdated);
cx.notify();
}
} else {
this.title_generation_failed = true;
cx.emit(TitleUpdated);
cx.notify();
}
});
}));
@@ -3889,7 +3897,7 @@ impl Thread {
pub fn set_title(&mut self, title: SharedString, cx: &mut Context<Self>) {
self.pending_title_generation = None;
self.title_generation_failed = false;
self.title_generation_error = None;
if Some(&title) != self.title.as_ref() {
self.title = Some(title);
cx.emit(TitleUpdated);
+11 -4
View File
@@ -5333,9 +5333,9 @@ impl AgentPanel {
let is_generating_title = native_thread
.as_ref()
.is_some_and(|thread| thread.read(cx).is_generating_title());
let title_generation_failed = native_thread
let title_generation_error = native_thread
.as_ref()
.is_some_and(|thread| thread.read(cx).has_failed_title_generation());
.and_then(|thread| thread.read(cx).title_generation_error());
if let Some(title_editor) = server_view_ref
.root_thread_view()
@@ -5374,7 +5374,7 @@ impl AgentPanel {
})
.child(title_editor);
if title_generation_failed {
if let Some(title_generation_error) = title_generation_error {
h_flex()
.w_full()
.gap_1()
@@ -5383,7 +5383,14 @@ impl AgentPanel {
IconButton::new("retry-thread-title", IconName::XCircle)
.icon_color(Color::Error)
.icon_size(IconSize::Small)
.tooltip(Tooltip::text("Title generation failed. Retry"))
.tooltip(move |_window, cx| {
Tooltip::with_meta(
"Title generation failed. Click to retry.",
None,
title_generation_error.clone(),
cx,
)
})
.on_click({
let conversation_view = conversation_view.clone();
let workspace = self.workspace.clone();