Enhanced PostHog Error Tracking with Detailed Messages (#6176)
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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
@@ -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 = {
|
||||||
|
|||||||
Reference in New Issue
Block a user