Refactor error handling and deduplicate error events

Updated error handling in chat commands to reduce redundant event emissions by logging errors instead. Enhanced error reporting for HTTP client and inference response failures. Added frontend logic to prevent duplicate error messages in the UI.
This commit is contained in:
cyrus
2026-03-27 18:09:39 +05:30
parent 9668f2ed23
commit 70bfd7231a
4 changed files with 82 additions and 26 deletions
+1 -1
Submodule skills updated: 0685a6af4e...e856b8e756
+1 -1
View File
@@ -4,7 +4,7 @@ version = 4
[[package]]
name = "OpenHuman"
version = "0.49.12"
version = "0.49.15"
dependencies = [
"aes-gcm",
"android_logger",
+66 -24
View File
@@ -436,15 +436,9 @@ pub async fn chat_send(
chat_state_arc.remove(&thread_id_clone);
if let Err(e) = result {
let _ = app_clone.emit(
"chat:error",
ChatErrorEvent {
thread_id: thread_id_clone,
message: e,
error_type: "inference".to_string(),
round: None,
},
);
// chat_send_inner already emits chat:error for known error paths.
// Only log here to avoid duplicate error events.
log::error!("[chat] chat_send_inner failed: {e}");
}
});
@@ -501,15 +495,9 @@ pub async fn chat_send(
chat_state_arc.remove(&thread_id_clone);
if let Err(e) = result {
let _ = app_clone.emit(
"chat:error",
ChatErrorEvent {
thread_id: thread_id_clone,
message: e,
error_type: "inference".to_string(),
round: None,
},
);
// chat_send_mobile already emits chat:error for known error paths.
// Only log here to avoid duplicate error events.
log::error!("[chat] chat_send_mobile failed: {e}");
}
});
@@ -546,7 +534,16 @@ async fn chat_send_inner(
let client = reqwest::Client::builder()
.use_rustls_tls()
.build()
.map_err(|e| format!("Failed to build HTTP client: {}", e))?;
.map_err(|e| {
let msg = format!("Failed to build HTTP client: {}", e);
let _ = app.emit("chat:error", ChatErrorEvent {
thread_id: thread_id.to_string(),
message: msg.clone(),
error_type: "network".to_string(),
round: None,
});
msg
})?;
// ── Step 1: Load AI context ─────────────────────────────────────────
let openclaw_context = load_openclaw_context(app);
@@ -775,7 +772,16 @@ async fn chat_send_inner(
let completion: ChatCompletionResponse = response
.json()
.await
.map_err(|e| format!("Failed to parse inference response: {}", e))?;
.map_err(|e| {
let msg = format!("Failed to parse inference response: {}", e);
let _ = app.emit("chat:error", ChatErrorEvent {
thread_id: thread_id.to_string(),
message: msg.clone(),
error_type: "inference".to_string(),
round: Some(round),
});
msg
})?;
// Accumulate token usage
if let Some(ref usage) = completion.usage {
@@ -786,7 +792,16 @@ async fn chat_send_inner(
let choice = completion
.choices
.first()
.ok_or_else(|| "No choices in inference response".to_string())?;
.ok_or_else(|| {
let msg = "No choices in inference response".to_string();
let _ = app.emit("chat:error", ChatErrorEvent {
thread_id: thread_id.to_string(),
message: msg.clone(),
error_type: "inference".to_string(),
round: Some(round),
});
msg
})?;
log::info!(
"[chat] Round {} — finish_reason={:?}, tool_calls={}",
@@ -1002,7 +1017,16 @@ async fn chat_send_mobile(
let client = reqwest::Client::builder()
.use_rustls_tls()
.build()
.map_err(|e| format!("Failed to build HTTP client: {}", e))?;
.map_err(|e| {
let msg = format!("Failed to build HTTP client: {}", e);
let _ = app.emit("chat:error", ChatErrorEvent {
thread_id: thread_id.to_string(),
message: msg.clone(),
error_type: "network".to_string(),
round: None,
});
msg
})?;
// ── Step 1: Load AI context ─────────────────────────────────────────
let openclaw_context = load_openclaw_context(app);
@@ -1146,7 +1170,16 @@ async fn chat_send_mobile(
let completion: ChatCompletionResponse = response
.json()
.await
.map_err(|e| format!("Failed to parse inference response: {}", e))?;
.map_err(|e| {
let msg = format!("Failed to parse inference response: {}", e);
let _ = app.emit("chat:error", ChatErrorEvent {
thread_id: thread_id.to_string(),
message: msg.clone(),
error_type: "inference".to_string(),
round: None,
});
msg
})?;
let (total_input_tokens, total_output_tokens) = completion
.usage
@@ -1156,7 +1189,16 @@ async fn chat_send_mobile(
let choice = completion
.choices
.first()
.ok_or_else(|| "No choices in inference response".to_string())?;
.ok_or_else(|| {
let msg = "No choices in inference response".to_string();
let _ = app.emit("chat:error", ChatErrorEvent {
thread_id: thread_id.to_string(),
message: msg.clone(),
error_type: "inference".to_string(),
round: None,
});
msg
})?;
let full_response = choice.message.content.clone().unwrap_or_default();
+14
View File
@@ -293,6 +293,20 @@ const Conversations = () => {
});
if (event.error_type !== 'cancelled') {
// Deduplicate: skip if the last message is already an error
const currentState = store.getState() as {
thread: { messagesByThreadId: Record<string, ThreadMessage[]> };
};
const threadMessages =
currentState.thread.messagesByThreadId[event.thread_id] || [];
const lastMsg = threadMessages[threadMessages.length - 1];
if (
lastMsg?.sender === 'agent' &&
lastMsg?.content === 'Something went wrong — please try again.'
) {
return;
}
dispatch(
addInferenceResponse({
content: 'Something went wrong — please try again.',