store an error

This commit is contained in:
Brandon Thomas committed 2026-04-01 19:55:50 -04:00
1 parent e7bb263755
commit 691e5109a5
9 files changed
+47 -19

No files matched your search

@@ -63,6 +63,7 @@ mod tests {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
@@ -222,6 +223,7 @@ mod tests {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
@@ -5,7 +5,7 @@ use crate::notification::generate_deduplication_key::generate_deduplication_key;
use crate::notification::notification_urgency::NotificationUrgency;
/// Details for a pager notification.
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct NotificationDetails {
/// Title or summary of the alert.
pub title: String,
@@ -42,6 +42,9 @@ pub struct NotificationDetails {
/// Third-party identifier associated with the event, if any.
pub third_party_id: Option<String>,
/// The error that triggered this notification, if any.
pub maybe_error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
}
impl NotificationDetails {
@@ -50,6 +53,7 @@ impl NotificationDetails {
}
/// Create a notification with a title and description.
#[deprecated(note = "Use NotificationDetailsBuilder::from_title() instead")]
pub fn with_title_and_description(title: String, description: String) -> Self {
Self {
title,
@@ -64,10 +68,12 @@ impl NotificationDetails {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
/// Create a notification with just a title.
#[deprecated(note = "Use NotificationDetailsBuilder::from_title() instead")]
pub fn with_title(title: String) -> Self {
Self {
title,
@@ -82,6 +88,7 @@ impl NotificationDetails {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
@@ -92,7 +99,11 @@ impl NotificationDetails {
/// - The error's source chain (if any)
/// - A backtrace (if available via `std::backtrace`)
/// - The timestamp of the event
pub fn from_error<E: Debug + Display>(error: &E) -> Self {
///
/// Note: this does not retain the error object itself. Use
/// `NotificationDetailsBuilder::from_title().set_error()` to attach an error.
#[deprecated(note = "Use NotificationDetailsBuilder::from_title().set_error() instead")]
pub fn from_error_info<E: Debug + Display>(error: &E) -> Self {
let title = format!("{}", error);
// Truncate the title to a reasonable length for alert titles.
@@ -147,12 +158,15 @@ impl NotificationDetails {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
/// Create a notification from an error, with a custom title prefix.
pub fn from_error_with_context<E: Debug + Display>(context: &str, error: &E) -> Self {
let mut notification = Self::from_error(error);
#[deprecated(note = "Use NotificationDetailsBuilder::from_title().set_error() instead")]
pub fn from_error_info_with_context<E: Debug + Display>(context: &str, error: &E) -> Self {
#[allow(deprecated)]
let mut notification = Self::from_error_info(error);
notification.title = format!("{}: {}", context, notification.title);
if notification.title.len() > 200 {
notification.title = format!("{}...", &notification.title[..197]);
@@ -17,6 +17,7 @@ pub struct NotificationDetailsBuilder {
media_file_token: Option<String>,
inference_job_token: Option<String>,
third_party_id: Option<String>,
maybe_error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
}
impl NotificationDetailsBuilder {
@@ -35,16 +36,20 @@ impl NotificationDetailsBuilder {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
/// Create a builder from an error.
/// Create a builder from an error's Display/Debug info.
///
/// Sets `is_from_error` to true and derives the summary and description
/// from the error, matching the behavior of `NotificationDetails::from_error`.
pub fn from_error<E: Debug + Display>(error: &E) -> Self {
// TODO(bt,2026-03-30): Clean this up
let details = NotificationDetails::from_error(error);
/// Sets `is_from_error` to true and derives the title and description
/// from the error. Does NOT retain the error object itself.
///
/// Use `from_title().set_error()` to attach the actual error.
#[deprecated(note = "Use from_title().set_error() instead")]
pub fn from_error_info<E: Debug + Display>(error: &E) -> Self {
#[allow(deprecated)]
let details = NotificationDetails::from_error_info(error);
Self {
title: details.title,
description: details.description,
@@ -58,9 +63,10 @@ impl NotificationDetailsBuilder {
media_file_token: None,
inference_job_token: None,
third_party_id: None,
maybe_error: None,
}
}
pub fn set_title(mut self, title: String) -> Self {
self.title = title;
self
@@ -111,6 +117,11 @@ impl NotificationDetailsBuilder {
self
}
pub fn set_error(mut self, error: Option<Box<dyn std::error::Error + Send + Sync + 'static>>) -> Self {
self.maybe_error = error;
self
}
pub fn build(self) -> NotificationDetails {
NotificationDetails {
title: self.title,
@@ -125,6 +136,7 @@ impl NotificationDetailsBuilder {
media_file_token: self.media_file_token,
inference_job_token: self.inference_job_token,
third_party_id: self.third_party_id,
maybe_error: self.maybe_error,
}
}
}
@@ -11,7 +11,7 @@ pub fn alert_pager_and_return_err<T>(
err: anyhow::Error,
job: Option<&PendingSeedance2ProJob>,
) -> anyhow::Result<T> {
let mut builder = NotificationDetailsBuilder::from_error(&err)
let mut builder = NotificationDetailsBuilder::from_error_info(&err)
.set_title(title.to_string())
.set_urgency(Some(NotificationUrgency::Medium));
@@ -85,7 +85,7 @@ pub async fn process_failed_job(
err,
);
let notification = NotificationDetailsBuilder::from_error(&err)
let notification = NotificationDetailsBuilder::from_error_info(&err)
.set_title("Seedance2Pro refund failed".to_string())
.set_inference_job_token(Some(job.job_token.to_string()))
.set_third_party_id(Some(job.order_id.to_string()))
@@ -140,7 +140,7 @@ pub async fn process_failed_job(
err
);
let notification = NotificationDetailsBuilder::from_error(&err)
let notification = NotificationDetailsBuilder::from_error_info(&err)
.set_title("Seedance2Pro mark job failed error".to_string())
.set_urgency(Some(NotificationUrgency::Medium))
.build();
@@ -9,7 +9,7 @@ pub fn alert_pager_and_return_err<T>(
title: &str,
err: anyhow::Error,
) -> anyhow::Result<T> {
let notification = NotificationDetailsBuilder::from_error(&err)
let notification = NotificationDetailsBuilder::from_error_info(&err)
.set_title(title.to_string())
.set_urgency(Some(NotificationUrgency::Medium))
.build();
@@ -222,7 +222,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
Err(err) => {
warn!("Regular session fallback also failed for user {:?}: {:?}", user_token, err);
if let Err(page_err) = server_state.pager.enqueue_page(
NotificationDetailsBuilder::from_error(&err)
NotificationDetailsBuilder::from_error_info(&err)
.set_title("Seedance 2.0 generation failed (whitelist + fallback)".to_string())
.set_urgency(Some(NotificationUrgency::High))
.build()
@@ -256,7 +256,7 @@ pub async fn seedance_2p0_multi_function_video_gen_handler(
Err(err) => {
warn!("Error calling seedance2pro generate_video: {:?}", err);
if let Err(page_err) = server_state.pager.enqueue_page(
NotificationDetailsBuilder::from_error(&err)
NotificationDetailsBuilder::from_error_info(&err)
.set_title("Seedance 2.0 generation failed".to_string())
.set_urgency(Some(NotificationUrgency::High))
.build()
@@ -120,7 +120,7 @@ pub async fn fal_webhook_handler(
if let Err(ref err) = result {
error!("FAL webhook error for request_id {}: {:?}", request_id, err);
let notification = NotificationDetailsBuilder::from_error(err)
let notification = NotificationDetailsBuilder::from_error_info(err)
.set_title("FAL webhook processing failed".to_string())
.set_description(Some(format!(
"FAL webhook failed for request_id: {}\n\nError: {:?}\n\nWebhook JSON Payload: {}",
@@ -35,7 +35,7 @@ pub async fn db_health_checker_thread(
Ok(_) => {},
}
let notification = NotificationDetailsBuilder::from_error(&database_error)
let notification = NotificationDetailsBuilder::from_error_info(&database_error)
.set_title("DB health check thread failed check".to_string())
.set_urgency(Some(NotificationUrgency::High))
.build();