Enhanced PostHog Error Tracking with Detailed Messages (#6176)

This commit is contained in:
Zane
2025-12-18 13:40:31 -08:00
committed by GitHub
parent 7ff3adcc5f
commit 70ac4b4322
8 changed files with 61 additions and 10 deletions
+15 -4
View File
@@ -147,6 +147,7 @@ async fn start_agent(
Ok(recipe) => Some(recipe), Ok(recipe) => Some(recipe),
Err(err) => { Err(err) => {
error!("Failed to decode recipe deeplink: {}", err); error!("Failed to decode recipe deeplink: {}", err);
goose::posthog::emit_error("recipe_deeplink_decode_failed", &err.to_string());
return Err(ErrorResponse { return Err(ErrorResponse {
message: err.to_string(), message: err.to_string(),
status: StatusCode::BAD_REQUEST, status: StatusCode::BAD_REQUEST,
@@ -179,6 +180,7 @@ async fn start_agent(
.await .await
.map_err(|err| { .map_err(|err| {
error!("Failed to create session: {}", err); error!("Failed to create session: {}", err);
goose::posthog::emit_error("session_create_failed", &err.to_string());
ErrorResponse { ErrorResponse {
message: format!("Failed to create session: {}", err), message: format!("Failed to create session: {}", err),
status: StatusCode::BAD_REQUEST, status: StatusCode::BAD_REQUEST,
@@ -233,6 +235,7 @@ async fn resume_agent(
.await .await
.map_err(|err| { .map_err(|err| {
error!("Failed to resume session {}: {}", payload.session_id, err); error!("Failed to resume session {}: {}", payload.session_id, err);
goose::posthog::emit_error("session_resume_failed", &err.to_string());
ErrorResponse { ErrorResponse {
message: format!("Failed to resume session: {}", err), message: format!("Failed to resume session: {}", err),
status: StatusCode::NOT_FOUND, status: StatusCode::NOT_FOUND,
@@ -304,6 +307,10 @@ async fn resume_agent(
async move { async move {
if let Err(e) = agent_ref.add_extension(config_clone.clone()).await { if let Err(e) = agent_ref.add_extension(config_clone.clone()).await {
warn!("Failed to load extension {}: {}", config_clone.name(), e); warn!("Failed to load extension {}: {}", config_clone.name(), e);
goose::posthog::emit_error(
"extension_load_failed",
&format!("{}: {}", config_clone.name(), e),
);
} }
Ok::<_, ErrorResponse>(()) Ok::<_, ErrorResponse>(())
} }
@@ -536,11 +543,15 @@ async fn agent_add_extension(
State(state): State<Arc<AppState>>, State(state): State<Arc<AppState>>,
Json(request): Json<AddExtensionRequest>, Json(request): Json<AddExtensionRequest>,
) -> Result<StatusCode, ErrorResponse> { ) -> Result<StatusCode, ErrorResponse> {
let extension_name = request.config.name();
let agent = state.get_agent(request.session_id).await?; let agent = state.get_agent(request.session_id).await?;
agent agent.add_extension(request.config).await.map_err(|e| {
.add_extension(request.config) goose::posthog::emit_error(
.await "extension_add_failed",
.map_err(|e| ErrorResponse::internal(format!("Failed to add extension: {}", e)))?; &format!("{}: {}", extension_name, e),
);
ErrorResponse::internal(format!("Failed to add extension: {}", e))
})?;
Ok(StatusCode::OK) Ok(StatusCode::OK)
} }
+4
View File
@@ -198,6 +198,7 @@ async fn create_recipe(
} }
Err(e) => { Err(e) => {
tracing::error!("Error details: {:?}", e); tracing::error!("Error details: {:?}", e);
goose::posthog::emit_error("recipe_create_failed", &e.to_string());
let error_response = CreateRecipeResponse { let error_response = CreateRecipeResponse {
recipe: None, recipe: None,
error: Some(format!("Failed to create recipe: {}", e)), error: Some(format!("Failed to create recipe: {}", e)),
@@ -224,6 +225,7 @@ async fn encode_recipe(
Ok(encoded) => Ok(Json(EncodeRecipeResponse { deeplink: encoded })), Ok(encoded) => Ok(Json(EncodeRecipeResponse { deeplink: encoded })),
Err(err) => { Err(err) => {
tracing::error!("Failed to encode recipe: {}", err); tracing::error!("Failed to encode recipe: {}", err);
goose::posthog::emit_error("recipe_encode_failed", &err.to_string());
Err(StatusCode::BAD_REQUEST) Err(StatusCode::BAD_REQUEST)
} }
} }
@@ -249,6 +251,7 @@ async fn decode_recipe(
}, },
Err(err) => { Err(err) => {
tracing::error!("Failed to decode deeplink: {}", err); tracing::error!("Failed to decode deeplink: {}", err);
goose::posthog::emit_error("recipe_decode_failed", &err.to_string());
Err(StatusCode::BAD_REQUEST) Err(StatusCode::BAD_REQUEST)
} }
} }
@@ -374,6 +377,7 @@ async fn schedule_recipe(
Ok(_) => Ok(StatusCode::OK), Ok(_) => Ok(StatusCode::OK),
Err(e) => { Err(e) => {
tracing::error!("Failed to schedule recipe: {}", e); tracing::error!("Failed to schedule recipe: {}", e);
goose::posthog::emit_error("recipe_schedule_failed", &e.to_string());
Err(StatusCode::INTERNAL_SERVER_ERROR) Err(StatusCode::INTERNAL_SERVER_ERROR)
} }
} }
@@ -361,6 +361,7 @@ async fn edit_message(
.await .await
.map_err(|e| { .map_err(|e| {
tracing::error!("Failed to copy session: {}", e); tracing::error!("Failed to copy session: {}", e);
goose::posthog::emit_error("session_copy_failed", &e.to_string());
StatusCode::INTERNAL_SERVER_ERROR StatusCode::INTERNAL_SERVER_ERROR
})?; })?;
@@ -368,6 +369,7 @@ async fn edit_message(
.await .await
.map_err(|e| { .map_err(|e| {
tracing::error!("Failed to truncate conversation: {}", e); tracing::error!("Failed to truncate conversation: {}", e);
goose::posthog::emit_error("session_truncate_failed", &e.to_string());
StatusCode::INTERNAL_SERVER_ERROR StatusCode::INTERNAL_SERVER_ERROR
})?; })?;
@@ -380,6 +382,7 @@ async fn edit_message(
.await .await
.map_err(|e| { .map_err(|e| {
tracing::error!("Failed to truncate conversation: {}", e); tracing::error!("Failed to truncate conversation: {}", e);
goose::posthog::emit_error("session_truncate_failed", &e.to_string());
StatusCode::INTERNAL_SERVER_ERROR StatusCode::INTERNAL_SERVER_ERROR
})?; })?;
+7 -2
View File
@@ -534,6 +534,10 @@ impl Agent {
.dispatch_tool_call(tool_call.clone(), cancellation_token.unwrap_or_default()) .dispatch_tool_call(tool_call.clone(), cancellation_token.unwrap_or_default())
.await; .await;
result.unwrap_or_else(|e| { result.unwrap_or_else(|e| {
crate::posthog::emit_error(
"tool_execution_failed",
&format!("{}: {}", tool_call.name, e),
);
ToolCallResult::from(Err(ErrorData::new( ToolCallResult::from(Err(ErrorData::new(
ErrorCode::INTERNAL_ERROR, ErrorCode::INTERNAL_ERROR,
e.to_string(), e.to_string(),
@@ -1242,7 +1246,7 @@ impl Agent {
} }
} }
Err(ref provider_err @ ProviderError::ContextLengthExceeded(_)) => { Err(ref provider_err @ ProviderError::ContextLengthExceeded(_)) => {
crate::posthog::emit_error(provider_err.telemetry_type()); crate::posthog::emit_error(provider_err.telemetry_type(), &provider_err.to_string());
compaction_attempts += 1; compaction_attempts += 1;
if compaction_attempts >= 2 { if compaction_attempts >= 2 {
@@ -1279,13 +1283,14 @@ impl Agent {
break; break;
} }
Err(e) => { Err(e) => {
crate::posthog::emit_error("compaction_failed", &e.to_string());
error!("Compaction failed: {}", e); error!("Compaction failed: {}", e);
break; break;
} }
} }
} }
Err(ref provider_err) => { Err(ref provider_err) => {
crate::posthog::emit_error(provider_err.telemetry_type()); crate::posthog::emit_error(provider_err.telemetry_type(), &provider_err.to_string());
error!("Error: {}", provider_err); error!("Error: {}", provider_err);
yield AgentEvent::Message( yield AgentEvent::Message(
Message::assistant().with_text( Message::assistant().with_text(
+4
View File
@@ -137,6 +137,10 @@ impl RetryManager {
"Maximum retry attempts ({}) exceeded", "Maximum retry attempts ({}) exceeded",
retry_config.max_retries retry_config.max_retries
); );
crate::posthog::emit_error(
"retry_max_exceeded",
&format!("Max retries ({}) exceeded", retry_config.max_retries),
);
return Ok(RetryResult::MaxAttemptsReached); return Ok(RetryResult::MaxAttemptsReached);
} }
+13 -2
View File
@@ -218,10 +218,17 @@ pub fn emit_session_started() {
pub struct ErrorContext { pub struct ErrorContext {
pub component: Option<String>, pub component: Option<String>,
pub action: Option<String>, pub action: Option<String>,
pub error_message: Option<String>,
} }
pub fn emit_error(error_type: &str) { pub fn emit_error(error_type: &str, error_message: &str) {
emit_error_with_context(error_type, ErrorContext::default()); emit_error_with_context(
error_type,
ErrorContext {
error_message: Some(error_message.to_string()),
..Default::default()
},
);
} }
pub fn emit_error_with_context(error_type: &str, context: ErrorContext) { pub fn emit_error_with_context(error_type: &str, context: ErrorContext) {
@@ -273,6 +280,10 @@ async fn send_error_event(
if let Some(action) = &context.action { if let Some(action) = &context.action {
event.insert_prop("action", action.as_str()).ok(); event.insert_prop("action", action.as_str()).ok();
} }
if let Some(error_message) = &context.error_message {
let sanitized = sanitize_string(error_message);
event.insert_prop("error_message", sanitized).ok();
}
if let Some(platform_version) = get_platform_version() { if let Some(platform_version) = get_platform_version() {
event.insert_prop("platform_version", platform_version).ok(); event.insert_prop("platform_version", platform_version).ok();
+4 -1
View File
@@ -260,7 +260,10 @@ impl Scheduler {
match result { match result {
Ok(_) => tracing::info!("Job '{}' completed", task_job_id), Ok(_) => tracing::info!("Job '{}' completed", task_job_id),
Err(e) => tracing::error!("Job '{}' failed: {}", task_job_id, e), Err(ref e) => {
tracing::error!("Job '{}' failed: {}", task_job_id, e);
crate::posthog::emit_error("scheduler_job_failed", &e.to_string());
}
} }
}) })
}) })
+11 -1
View File
@@ -43,7 +43,7 @@ import { NoProviderOrModelError, useAgent } from './hooks/useAgent';
import { useNavigation } from './hooks/useNavigation'; import { useNavigation } from './hooks/useNavigation';
import { errorMessage } from './utils/conversionUtils'; import { errorMessage } from './utils/conversionUtils';
import { usePageViewTracking } from './hooks/useAnalytics'; import { usePageViewTracking } from './hooks/useAnalytics';
import { trackOnboardingCompleted } from './utils/analytics'; import { trackOnboardingCompleted, trackErrorWithContext } from './utils/analytics';
function PageViewTracker() { function PageViewTracker() {
usePageViewTracking(); usePageViewTracking();
@@ -129,6 +129,11 @@ const PairRouteWrapper = ({
setActiveSessionId(newSession.id); setActiveSessionId(newSession.id);
} catch (error) { } catch (error) {
console.error('[PairRouteWrapper] Failed to create session:', error); console.error('[PairRouteWrapper] Failed to create session:', error);
trackErrorWithContext(error, {
component: 'PairRouteWrapper',
action: 'create_session',
recoverable: true,
});
} finally { } finally {
setIsCreatingSession(false); setIsCreatingSession(false);
} }
@@ -428,6 +433,11 @@ export function AppInner() {
}); });
} catch (error) { } catch (error) {
console.error('Unexpected error opening shared session:', error); console.error('Unexpected error opening shared session:', error);
trackErrorWithContext(error, {
component: 'AppInner',
action: 'open_shared_session',
recoverable: true,
});
// Navigate to shared session view with error // Navigate to shared session view with error
const shareToken = link.replace('goose://sessions/', ''); const shareToken = link.replace('goose://sessions/', '');
const options = { const options = {