22
33If you'd like to literally catch all errors in your canvas Power Apps, simply copy and paste the code shown below.
44
5- Related content: https://youtu.be/8qcPq4peows 📺
5+ Related content: https://youtu.be/8qcPq4peows
66
77## Prerequisites
88
@@ -16,6 +16,25 @@ Related content: https://youtu.be/8qcPq4peows 📺
1616
1717---
1818
19+ ## How It Works: Signature-Based Deduplication
20+
21+ This error handler uses ** smart deduplication** to prevent email floods:
22+
23+ | What Happens | Old Behavior | New Behavior |
24+ | --------------| --------------| --------------|
25+ | Error A occurs 50× | 50 emails | 1 email |
26+ | Error B occurs 10× | 10 emails | 1 email (includes "Error A: 50×") |
27+ | ** Total** | ** 60 emails** | ** 2 emails** |
28+
29+ ### The Approach
30+
31+ 1 . ** Unique Signatures** - Each error gets a signature: ` Screen|Source|Message `
32+ 2 . ** Collection Tracking** - ` colErrorSignatures ` stores unique errors with occurrence counts
33+ 3 . ** Smart Emailing** - Only sends email on ** first occurrence** of each unique error
34+ 4 . ** Full Context** - Every email includes the ** complete session error summary** with counts
35+
36+ ---
37+
1938## Step 1: Add Named Formulas to ` App.Formulas `
2039
2140Copy this code block to your ** App.Formulas** property. Update the values as needed for your application.
@@ -53,115 +72,225 @@ Copy this code block to your **App.OnError** property. This code can remain unmo
5372
5473``` PowerFx
5574// ============================================================================
56- // ERROR HANDLER - Add to App.OnError
75+ // ERROR HANDLER WITH SIGNATURE-BASED DEDUPLICATION - Add to App.OnError
76+ // ============================================================================
77+ // Only sends email on FIRST occurrence of each unique error.
78+ // Subsequent occurrences just increment the counter - no spam!
5779// ============================================================================
5880With(
5981 {
82+ ScreenName: App.ActiveScreen.Name,
83+ MyUsersName: User().FullName,
84+ MyUsersEmail: User().Email,
85+ CurrentTime: Now(),
86+ LightGrayColorHexBG: "background-color:" & fxLightGrayColor & ";",
87+ CountBefore: CountRows(colErrorSignatures),
6088 SubjectLine: Concatenate(
6189 "Error(s) occurred in the ",
6290 fxApplicationName,
6391 " application for ",
6492 User().FullName
65- ),
66- HowManyErrors: CountRows(AllErrors) + CountRows(colAppErrors),
67- LightGrayColorHexBG: "background-color:" & fxLightGrayColor & ";",
68- ScreenName: App.ActiveScreen.Name,
69- MyUsersName: User().FullName,
70- MyUsersEmail: User().Email
93+ )
7194 },
72- // Collect all errors into the colAppErrors collection
73- Collect(
74- colAppErrors,
75- ForAll(
76- AllErrors As My,
95+ // Process each error - add new signatures or update existing counts
96+ ForAll(
97+ AllErrors As Err,
98+ With(
7799 {
78- Kind: My.Kind,
79- Message: Text(My.Message),
80- Source: Text(My.Source),
81- Observed: Text(My.Observed),
82- HttpResponse: Text(My.Details.HttpResponse),
83- HttpStatusCode: Text(My.Details.HttpStatusCode),
84- TimeStamp: Text(Now(), "MM/dd/yyyy hh:mm:ss:ffff AM/PM"),
85- Screen: ScreenName,
86- UserEmail: MyUsersEmail,
87- UsersName: MyUsersName
88- }
100+ // Generate unique signature: Screen|Source|Message
101+ ErrSignature: ScreenName & "|" & Text(Err.Source) & "|" & Text(Err.Message)
102+ },
103+ If(
104+ IsBlank(LookUp(colErrorSignatures, Signature = ErrSignature)),
105+ // ═══════════════════════════════════════════════════════════
106+ // NEW UNIQUE ERROR - Add to tracking collection
107+ // ═══════════════════════════════════════════════════════════
108+ Collect(
109+ colErrorSignatures,
110+ {
111+ Signature: ErrSignature,
112+ Screen: ScreenName,
113+ Source: Text(Err.Source),
114+ Kind: Err.Kind,
115+ Message: Text(Err.Message),
116+ Observed: Text(Err.Observed),
117+ HttpResponse: Text(Err.Details.HttpResponse),
118+ HttpStatusCode: Text(Err.Details.HttpStatusCode),
119+ Occurrences: 1,
120+ FirstOccurrence: Text(CurrentTime, "MM/dd/yyyy hh:mm:ss AM/PM"),
121+ LastOccurrence: Text(CurrentTime, "MM/dd/yyyy hh:mm:ss AM/PM"),
122+ UserEmail: MyUsersEmail,
123+ UsersName: MyUsersName
124+ }
125+ ),
126+ // ═══════════════════════════════════════════════════════════
127+ // EXISTING ERROR - Just increment counter, NO EMAIL
128+ // ═══════════════════════════════════════════════════════════
129+ Patch(
130+ colErrorSignatures,
131+ LookUp(colErrorSignatures, Signature = ErrSignature),
132+ {
133+ Occurrences: LookUp(colErrorSignatures, Signature = ErrSignature).Occurrences + 1,
134+ LastOccurrence: Text(CurrentTime, "MM/dd/yyyy hh:mm:ss AM/PM")
135+ }
136+ )
137+ )
89138 )
90139 );
91- // Send email notification to developer(s)
92- Office365Outlook.SendEmailV2(
93- fxErrorHandlerEmail,
94- SubjectLine,
95- $"<html><body>
96- <h3>Error Report for {MyUsersName} ({MyUsersEmail})</h3>
97- <table style='width:100%;' border='1' cellpadding='10' cellspacing='0'>
98- <tr style='{LightGrayColorHexBG} width:100%;'>
99- <th>Time Stamp</th>
100- <th>Screen Name</th>
101- <th>Kind</th>
102- <th>Source</th>
103- <th>Message</th>
104- <th>Observed</th>
105- <th>Http Response</th>
106- <th>Http Status Code</th>
107- </tr>" &
108- Concat(
109- colAppErrors,
110- $"<tr>
111- <td>{TimeStamp}</td>
112- <td>{Screen}</td>
113- <td>{Kind}</td>
114- <td>{Source}</td>
115- <td>{Message}</td>
116- <td>{Observed}</td>
117- <td>{HttpResponse}</td>
118- <td>{HttpStatusCode}</td>
119- </tr>"
120- ) & $"
121- <tr>
122- <td colspan='8' style='{LightGrayColorHexBG}'>
123- <div>
124- From Application: <a href='{fxApplicationURL}'>{fxApplicationName}</a>
125- </div>
126- </td>
127- </tr>
128- </table>
129- </body></html>"
140+ // ═══════════════════════════════════════════════════════════════════════
141+ // ONLY SEND EMAIL IF NEW UNIQUE ERROR(S) WERE ADDED
142+ // ═══════════════════════════════════════════════════════════════════════
143+ If(
144+ CountRows(colErrorSignatures) > CountBefore,
145+ Office365Outlook.SendEmailV2(
146+ fxErrorHandlerEmail,
147+ SubjectLine,
148+ $"<html><body>
149+ <h3>Error Report for {MyUsersName} ({MyUsersEmail})</h3>
150+ <p><strong>New unique error detected.</strong> Full session error summary below:</p>
151+ <table style='width:100%;' border='1' cellpadding='8' cellspacing='0'>
152+ <tr style='{LightGrayColorHexBG}'>
153+ <th style='text-align:center;'>Count</th>
154+ <th>Screen</th>
155+ <th>Kind</th>
156+ <th>Source</th>
157+ <th>Message</th>
158+ <th>First Seen</th>
159+ <th>Last Seen</th>
160+ </tr>" &
161+ Concat(
162+ colErrorSignatures,
163+ $"<tr>
164+ <td style='text-align:center;font-weight:bold;font-size:1.2em;'>{Occurrences}×</td>
165+ <td>{Screen}</td>
166+ <td>{Kind}</td>
167+ <td>{Source}</td>
168+ <td>{Message}</td>
169+ <td style='font-size:0.9em;'>{FirstOccurrence}</td>
170+ <td style='font-size:0.9em;'>{LastOccurrence}</td>
171+ </tr>"
172+ ) & $"
173+ <tr>
174+ <td colspan='7' style='{LightGrayColorHexBG}'>
175+ <div>
176+ <strong>Total unique errors:</strong> {CountRows(colErrorSignatures)} |
177+ <strong>Total occurrences:</strong> {Sum(colErrorSignatures, Occurrences)}
178+ </div>
179+ <div style='margin-top:8px;'>
180+ From Application: <a href='{fxApplicationURL}'>{fxApplicationName}</a>
181+ </div>
182+ </td>
183+ </tr>
184+ </table>
185+ </body></html>"
186+ )
130187 );
131188);
132189```
133190
134191---
135192
193+ ## Collection Schema: ` colErrorSignatures `
194+
195+ This collection is automatically created and managed by the error handler:
196+
197+ | Field Name | Type | Description |
198+ | -----------------| --------| -------------|
199+ | Signature | Text | Unique key: ` Screen\|Source\|Message ` |
200+ | Screen | Text | Screen where error occurred |
201+ | Source | Text | Control/function that caused error |
202+ | Kind | Text | Error type (Sync, Network, etc.) |
203+ | Message | Text | Error message text |
204+ | Observed | Text | Where error was observed |
205+ | HttpResponse | Text | HTTP response (if applicable) |
206+ | HttpStatusCode | Text | HTTP status code (if applicable) |
207+ | Occurrences | Number | How many times this error occurred |
208+ | FirstOccurrence | Text | Timestamp of first occurrence |
209+ | LastOccurrence | Text | Timestamp of most recent occurrence |
210+ | UserEmail | Text | User's email address |
211+ | UsersName | Text | User's display name |
212+
213+ ---
214+
215+ ## Optional: View Error Collection in App
216+
217+ Add a gallery to a debug/admin screen to see all tracked errors:
218+
219+ ``` PowerFx
220+ // Gallery Items property
221+ colErrorSignatures
222+
223+ // Useful label formulas for the gallery template
224+ ThisItem.Occurrences & "× - " & ThisItem.Message
225+ "First: " & ThisItem.FirstOccurrence
226+ "Last: " & ThisItem.LastOccurrence
227+ ```
228+
229+ ---
230+
231+ ## Optional: Clear Errors on App Start
232+
233+ Add to ` App.OnStart ` if you want a fresh collection each session:
234+
235+ ``` PowerFx
236+ Clear(colErrorSignatures);
237+ ```
238+
239+ ---
240+
136241## Optional: Persist Errors to a Data Source
137242
138243If you'd like to store errors in a database or SharePoint list for historical tracking:
139244
140245### Step 1: Create a Data Source
141246
142- Create a table/list named ` PowerAppsErrors ` with these fields (all single-line text) :
247+ Create a table/list named ` PowerAppsErrors ` with these fields:
143248
144- | Field Name | Type |
145- | ----------------| --------|
146- | Kind | Text |
147- | Message | Text |
148- | Source | Text |
149- | Observed | Text |
150- | HttpResponse | Text |
151- | HttpStatusCode | Text |
152- | TimeStamp | Text |
153- | Screen | Text |
154- | UserEmail | Text |
155- | UsersName | Text |
249+ | Field Name | Type |
250+ | -----------------| --------|
251+ | Signature | Text |
252+ | Screen | Text |
253+ | Source | Text |
254+ | Kind | Text |
255+ | Message | Text |
256+ | Observed | Text |
257+ | HttpResponse | Text |
258+ | HttpStatusCode | Text |
259+ | Occurrences | Number |
260+ | FirstOccurrence | Text |
261+ | LastOccurrence | Text |
262+ | UserEmail | Text |
263+ | UsersName | Text |
156264
157- ### Step 2: Add to End of App.OnError
265+ ### Step 2: Persist on App Close
158266
159- Add these lines at the end of the ` App.OnError ` code (after the ` Office365Outlook.SendEmailV2 ` call) :
267+ Add to a "Save & Exit" button or similar :
160268
161269``` PowerFx
162- // Persist errors to data source and clear collection
163- Patch(PowerAppsErrors, colAppErrors);
164- Clear(colAppErrors);
270+ // Persist all unique errors with their occurrence counts
271+ ForAll(
272+ colErrorSignatures,
273+ Patch(
274+ PowerAppsErrors,
275+ Defaults(PowerAppsErrors),
276+ {
277+ Signature: Signature,
278+ Screen: Screen,
279+ Source: Source,
280+ Kind: Kind,
281+ Message: Message,
282+ Observed: Observed,
283+ HttpResponse: HttpResponse,
284+ HttpStatusCode: HttpStatusCode,
285+ Occurrences: Occurrences,
286+ FirstOccurrence: FirstOccurrence,
287+ LastOccurrence: LastOccurrence,
288+ UserEmail: UserEmail,
289+ UsersName: UsersName
290+ }
291+ )
292+ );
293+ Clear(colErrorSignatures);
165294```
166295
167296---
0 commit comments