Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.4k
fix: improve error labelling, grouping, and stack traces in the Errors feature#4225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| area: webapp | ||
| type: fix | ||
| --- | ||
| The Errors page now shows better details for each error. Errors that don't carry a message — such as errors thrown without a message, or values thrown that aren't `Error` objects — get a meaningful title instead of all reading "Unknown error", and are grouped by their name (or value) rather than collapsed into a single group. The error type now shows the actual error name, and stack traces now appear where previously they were missing. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12,7 +12,11 @@ export function calculateErrorFingerprint(error: unknown): string { | ||
| // 2. It won't be an instanceof Error because it's from the database. | ||
| const errorObj = error as any; | ||
| const errorType = String(errorObj.type || errorObj.name || "Error"); | ||
| const message = String(errorObj.message || ""); | ||
| // Fall back to the error class name, then the raw serialized value, so | ||
| // messageless errors (e.g. tagged errors) and non-Error throws (strings, | ||
| // plain objects) still group by something distinctive instead of collapsing | ||
| // into a single fingerprint. Message-bearing errors are unaffected. | ||
| const message = String(errorObj.message || errorObj.name || errorObj.raw || ""); | ||
| const stack = String(errorObj.stack || errorObj.stacktrace || ""); | ||
matt-aitken marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| // Normalize message to group similar errors | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,192 @@ | ||
| -- +goose Up | ||
| -- Fix how the error materialized views derive their display columns from the | ||
| -- stored error JSON: | ||
| -- * error_type: use the real class name (or internal code), not the generic | ||
| -- serialization tag (BUILT_IN_ERROR / STRING_ERROR / ...). | ||
| -- * error_message: fall back name -> raw before 'Unknown error' so messageless | ||
| -- errors (tagged errors) and non-Error throws still get a meaningful title. | ||
| -- * stack trace: read error.data.stackTrace (the stored field), not | ||
| -- error.data.stack, which was always empty. | ||
| -- Display-only. Only affects rows inserted after this migration. | ||
| ALTER TABLE trigger_dev.errors_mv_v1 MODIFY QUERY | ||
| SELECT | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint, | ||
| any(coalesce(nullIf(toString(error.data.name), ''), nullIf(toString(error.data.code), ''), 'Error')) as error_type, | ||
matt-aitken marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| any(coalesce( | ||
| nullIf(substring(toString(error.data.message), 1, 500), ''), | ||
| nullIf(toString(error.data.name), ''), | ||
| nullIf(substring(toString(error.data.raw), 1, 500), ''), | ||
| 'Unknown error' | ||
| )) as error_message, | ||
matt-aitken marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| any(coalesce(substring(toString(error.data.stackTrace), 1, 2000), '')) as sample_stack_trace, | ||
| toDateTime(max(created_at)) as last_seen_date, | ||
| min(created_at) as first_seen, | ||
| max(created_at) as last_seen, | ||
| sumState(toUInt64(1)) as occurrence_count, | ||
| uniqState(task_version) as affected_task_versions, | ||
| anyState(run_id) as sample_run_id, | ||
| anyState(friendly_id) as sample_friendly_id, | ||
| sumMapState([status], [toUInt64(1)]) as status_distribution | ||
| FROM trigger_dev.task_runs_v2 | ||
| WHERE | ||
| error_fingerprint != '' | ||
| AND status IN ('SYSTEM_FAILURE', 'CRASHED', 'INTERRUPTED', 'COMPLETED_WITH_ERRORS', 'TIMED_OUT') | ||
| AND _is_deleted = 0 | ||
| GROUP BY | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint; | ||
| ALTER TABLE trigger_dev.error_occurrences_mv_v1 MODIFY QUERY | ||
| SELECT | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint, | ||
| task_version, | ||
| toStartOfMinute (created_at) as minute, | ||
| any ( | ||
| coalesce( | ||
| nullIf(toString (error.data.name), ''), | ||
| nullIf(toString (error.data.code), ''), | ||
| 'Error' | ||
| ) | ||
| ) as error_type, | ||
| any ( | ||
| coalesce( | ||
| nullIf(substring(toString (error.data.message), 1, 500), ''), | ||
| nullIf(toString (error.data.name), ''), | ||
| nullIf(substring(toString (error.data.raw), 1, 500), ''), | ||
| 'Unknown error' | ||
| ) | ||
| ) as error_message, | ||
| any ( | ||
| coalesce( | ||
| substring(toString (error.data.stackTrace), 1, 2000), | ||
| '' | ||
| ) | ||
| ) as stack_trace, | ||
| count() as count | ||
| FROM | ||
| trigger_dev.task_runs_v2 | ||
| WHERE | ||
| error_fingerprint != '' | ||
| AND status IN ( | ||
| 'SYSTEM_FAILURE', | ||
| 'CRASHED', | ||
| 'INTERRUPTED', | ||
| 'COMPLETED_WITH_ERRORS', | ||
| 'TIMED_OUT' | ||
| ) | ||
| AND _is_deleted = 0 | ||
| GROUP BY | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint, | ||
| task_version, | ||
| minute; | ||
| -- +goose Down | ||
| ALTER TABLE trigger_dev.errors_mv_v1 MODIFY QUERY | ||
| SELECT | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint, | ||
| any(coalesce(nullIf(toString(error.data.type), ''), nullIf(toString(error.data.name), ''), 'Error')) as error_type, | ||
| any(coalesce(nullIf(substring(toString(error.data.message), 1, 500), ''), 'Unknown error')) as error_message, | ||
| any(coalesce(substring(toString(error.data.stack), 1, 2000), '')) as sample_stack_trace, | ||
| toDateTime(max(created_at)) as last_seen_date, | ||
| min(created_at) as first_seen, | ||
| max(created_at) as last_seen, | ||
| sumState(toUInt64(1)) as occurrence_count, | ||
| uniqState(task_version) as affected_task_versions, | ||
| anyState(run_id) as sample_run_id, | ||
| anyState(friendly_id) as sample_friendly_id, | ||
| sumMapState([status], [toUInt64(1)]) as status_distribution | ||
| FROM trigger_dev.task_runs_v2 | ||
| WHERE | ||
| error_fingerprint != '' | ||
| AND status IN ('SYSTEM_FAILURE', 'CRASHED', 'INTERRUPTED', 'COMPLETED_WITH_ERRORS', 'TIMED_OUT') | ||
| AND _is_deleted = 0 | ||
| GROUP BY | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint; | ||
| ALTER TABLE trigger_dev.error_occurrences_mv_v1 MODIFY QUERY | ||
| SELECT | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint, | ||
| task_version, | ||
| toStartOfMinute (created_at) as minute, | ||
| any ( | ||
| coalesce( | ||
| nullIf(toString (error.data.type), ''), | ||
| nullIf(toString (error.data.name), ''), | ||
| 'Error' | ||
| ) | ||
| ) as error_type, | ||
| any ( | ||
| coalesce( | ||
| nullIf( | ||
| substring(toString (error.data.message), 1, 500), | ||
| '' | ||
| ), | ||
| 'Unknown error' | ||
| ) | ||
| ) as error_message, | ||
| any ( | ||
| coalesce( | ||
| substring(toString (error.data.stack), 1, 2000), | ||
| '' | ||
| ) | ||
| ) as stack_trace, | ||
| count() as count | ||
| FROM | ||
| trigger_dev.task_runs_v2 | ||
| WHERE | ||
| error_fingerprint != '' | ||
| AND status IN ( | ||
| 'SYSTEM_FAILURE', | ||
| 'CRASHED', | ||
| 'INTERRUPTED', | ||
| 'COMPLETED_WITH_ERRORS', | ||
| 'TIMED_OUT' | ||
| ) | ||
| AND _is_deleted = 0 | ||
| GROUP BY | ||
| organization_id, | ||
| project_id, | ||
| environment_id, | ||
| task_identifier, | ||
| error_fingerprint, | ||
| task_version, | ||
| minute; | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.