@luquimbo/bi-superpowers 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/.claude-plugin/plugin.json +8 -0
  2. package/.mcp.json +25 -0
  3. package/AGENTS.md +244 -0
  4. package/CHANGELOG.md +265 -0
  5. package/LICENSE +21 -0
  6. package/README.md +211 -0
  7. package/bin/build-plugin.js +30 -0
  8. package/bin/cli.js +1064 -0
  9. package/bin/commands/add.js +533 -0
  10. package/bin/commands/add.test.js +77 -0
  11. package/bin/commands/build-desktop.js +166 -0
  12. package/bin/commands/changelog.js +443 -0
  13. package/bin/commands/diff.js +325 -0
  14. package/bin/commands/lint.js +419 -0
  15. package/bin/commands/lint.test.js +103 -0
  16. package/bin/commands/mcp-setup.js +246 -0
  17. package/bin/commands/pull.js +287 -0
  18. package/bin/commands/pull.test.js +36 -0
  19. package/bin/commands/push.js +231 -0
  20. package/bin/commands/push.test.js +14 -0
  21. package/bin/commands/search.js +344 -0
  22. package/bin/commands/search.test.js +115 -0
  23. package/bin/commands/setup.js +545 -0
  24. package/bin/commands/setup.test.js +46 -0
  25. package/bin/commands/sync-profile.js +405 -0
  26. package/bin/commands/sync-profile.test.js +14 -0
  27. package/bin/commands/sync-source.js +418 -0
  28. package/bin/commands/sync-source.test.js +14 -0
  29. package/bin/commands/watch.js +206 -0
  30. package/bin/lib/generators/claude-plugin.js +266 -0
  31. package/bin/lib/generators/claude-plugin.test.js +110 -0
  32. package/bin/lib/generators/index.js +116 -0
  33. package/bin/lib/generators/shared.js +282 -0
  34. package/bin/lib/licensing/index.js +35 -0
  35. package/bin/lib/licensing/storage.js +364 -0
  36. package/bin/lib/licensing/storage.test.js +55 -0
  37. package/bin/lib/licensing/validator.js +213 -0
  38. package/bin/lib/licensing/validator.test.js +137 -0
  39. package/bin/lib/microsoft-mcp.js +176 -0
  40. package/bin/lib/microsoft-mcp.test.js +106 -0
  41. package/bin/lib/skills.js +84 -0
  42. package/bin/mcp/powerbi-modeling-launcher.js +38 -0
  43. package/bin/postinstall.js +44 -0
  44. package/bin/utils/errors.js +159 -0
  45. package/bin/utils/git.js +298 -0
  46. package/bin/utils/logger.js +142 -0
  47. package/bin/utils/mcp-detect.js +274 -0
  48. package/bin/utils/mcp-detect.test.js +105 -0
  49. package/bin/utils/pbix.js +305 -0
  50. package/bin/utils/pbix.test.js +37 -0
  51. package/bin/utils/profiles.js +312 -0
  52. package/bin/utils/projects.js +168 -0
  53. package/bin/utils/readline.js +206 -0
  54. package/bin/utils/readline.test.js +47 -0
  55. package/bin/utils/tui.js +314 -0
  56. package/bin/utils/tui.test.js +127 -0
  57. package/commands/contributions.md +265 -0
  58. package/commands/data-model-design.md +468 -0
  59. package/commands/dax-doctor.md +248 -0
  60. package/commands/fabric-scripts.md +452 -0
  61. package/commands/migration-assistant.md +290 -0
  62. package/commands/model-documenter.md +242 -0
  63. package/commands/pbi-connect.md +239 -0
  64. package/commands/project-kickoff.md +905 -0
  65. package/commands/report-layout.md +296 -0
  66. package/commands/rls-design.md +533 -0
  67. package/commands/theme-tweaker.md +624 -0
  68. package/config.example.json +23 -0
  69. package/config.json +23 -0
  70. package/desktop-extension/manifest.json +37 -0
  71. package/desktop-extension/package.json +10 -0
  72. package/desktop-extension/server.js +95 -0
  73. package/docs/openrouter-free-models.md +92 -0
  74. package/library/examples/README.md +151 -0
  75. package/library/examples/finance-reporting/README.md +351 -0
  76. package/library/examples/finance-reporting/data-model.md +267 -0
  77. package/library/examples/finance-reporting/measures.dax +557 -0
  78. package/library/examples/hr-analytics/README.md +371 -0
  79. package/library/examples/hr-analytics/data-model.md +315 -0
  80. package/library/examples/hr-analytics/measures.dax +460 -0
  81. package/library/examples/marketing-analytics/README.md +37 -0
  82. package/library/examples/marketing-analytics/data-model.md +62 -0
  83. package/library/examples/marketing-analytics/measures.dax +110 -0
  84. package/library/examples/retail-analytics/README.md +439 -0
  85. package/library/examples/retail-analytics/data-model.md +288 -0
  86. package/library/examples/retail-analytics/measures.dax +481 -0
  87. package/library/examples/supply-chain/README.md +37 -0
  88. package/library/examples/supply-chain/data-model.md +69 -0
  89. package/library/examples/supply-chain/measures.dax +77 -0
  90. package/library/examples/udf-library/README.md +228 -0
  91. package/library/examples/udf-library/functions.dax +571 -0
  92. package/library/snippets/dax/README.md +292 -0
  93. package/library/snippets/dax/business-domains.md +576 -0
  94. package/library/snippets/dax/calculate-patterns.md +276 -0
  95. package/library/snippets/dax/calculation-groups.md +489 -0
  96. package/library/snippets/dax/error-handling.md +495 -0
  97. package/library/snippets/dax/iterators-and-aggregations.md +474 -0
  98. package/library/snippets/dax/kpis-and-metrics.md +293 -0
  99. package/library/snippets/dax/rankings-and-topn.md +235 -0
  100. package/library/snippets/dax/security-patterns.md +413 -0
  101. package/library/snippets/dax/text-and-formatting.md +316 -0
  102. package/library/snippets/dax/time-intelligence.md +196 -0
  103. package/library/snippets/dax/user-defined-functions.md +477 -0
  104. package/library/snippets/dax/virtual-tables.md +546 -0
  105. package/library/snippets/excel-formulas/README.md +84 -0
  106. package/library/snippets/excel-formulas/aggregations.md +330 -0
  107. package/library/snippets/excel-formulas/dates-and-times.md +361 -0
  108. package/library/snippets/excel-formulas/dynamic-arrays.md +314 -0
  109. package/library/snippets/excel-formulas/lookups.md +169 -0
  110. package/library/snippets/excel-formulas/text-functions.md +363 -0
  111. package/library/snippets/governance/naming-conventions.md +97 -0
  112. package/library/snippets/governance/review-checklists.md +107 -0
  113. package/library/snippets/power-query/README.md +389 -0
  114. package/library/snippets/power-query/api-integration.md +707 -0
  115. package/library/snippets/power-query/connections.md +434 -0
  116. package/library/snippets/power-query/data-cleaning.md +298 -0
  117. package/library/snippets/power-query/error-handling.md +526 -0
  118. package/library/snippets/power-query/parameters.md +350 -0
  119. package/library/snippets/power-query/performance.md +506 -0
  120. package/library/snippets/power-query/transformations.md +330 -0
  121. package/library/snippets/report-design/accessibility.md +78 -0
  122. package/library/snippets/report-design/chart-selection.md +54 -0
  123. package/library/snippets/report-design/layout-patterns.md +87 -0
  124. package/library/templates/data-models/README.md +93 -0
  125. package/library/templates/data-models/finance-model.md +627 -0
  126. package/library/templates/data-models/retail-star-schema.md +473 -0
  127. package/library/templates/excel/README.md +83 -0
  128. package/library/templates/excel/budget-tracker.md +432 -0
  129. package/library/templates/excel/data-entry-form.md +533 -0
  130. package/library/templates/power-bi/README.md +72 -0
  131. package/library/templates/power-bi/finance-report.md +449 -0
  132. package/library/templates/power-bi/kpi-scorecard.md +461 -0
  133. package/library/templates/power-bi/sales-dashboard.md +281 -0
  134. package/library/themes/excel/README.md +436 -0
  135. package/library/themes/power-bi/README.md +271 -0
  136. package/library/themes/power-bi/accessible.json +307 -0
  137. package/library/themes/power-bi/bi-superpowers-default.json +858 -0
  138. package/library/themes/power-bi/corporate-blue.json +291 -0
  139. package/library/themes/power-bi/dark-mode.json +291 -0
  140. package/library/themes/power-bi/minimal.json +292 -0
  141. package/library/themes/power-bi/print-friendly.json +309 -0
  142. package/package.json +93 -0
  143. package/skills/contributions/SKILL.md +267 -0
  144. package/skills/data-model-design/SKILL.md +470 -0
  145. package/skills/data-modeling/SKILL.md +254 -0
  146. package/skills/data-quality/SKILL.md +664 -0
  147. package/skills/dax/SKILL.md +708 -0
  148. package/skills/dax-doctor/SKILL.md +250 -0
  149. package/skills/dax-udf/SKILL.md +489 -0
  150. package/skills/deployment/SKILL.md +320 -0
  151. package/skills/excel-formulas/SKILL.md +463 -0
  152. package/skills/fabric-scripts/SKILL.md +454 -0
  153. package/skills/fast-standard/SKILL.md +509 -0
  154. package/skills/governance/SKILL.md +205 -0
  155. package/skills/migration-assistant/SKILL.md +292 -0
  156. package/skills/model-documenter/SKILL.md +244 -0
  157. package/skills/pbi-connect/SKILL.md +241 -0
  158. package/skills/power-query/SKILL.md +406 -0
  159. package/skills/project-kickoff/SKILL.md +907 -0
  160. package/skills/query-performance/SKILL.md +480 -0
  161. package/skills/report-design/SKILL.md +207 -0
  162. package/skills/report-layout/SKILL.md +298 -0
  163. package/skills/rls-design/SKILL.md +535 -0
  164. package/skills/semantic-model/SKILL.md +237 -0
  165. package/skills/testing-validation/SKILL.md +643 -0
  166. package/skills/theme-tweaker/SKILL.md +626 -0
  167. package/src/content/base.md +237 -0
  168. package/src/content/mcp-requirements.json +69 -0
  169. package/src/content/routing.md +203 -0
  170. package/src/content/skills/contributions.md +259 -0
  171. package/src/content/skills/data-model-design.md +462 -0
  172. package/src/content/skills/data-modeling.md +246 -0
  173. package/src/content/skills/data-quality.md +656 -0
  174. package/src/content/skills/dax-doctor.md +242 -0
  175. package/src/content/skills/dax-udf.md +481 -0
  176. package/src/content/skills/dax.md +700 -0
  177. package/src/content/skills/deployment.md +312 -0
  178. package/src/content/skills/excel-formulas.md +455 -0
  179. package/src/content/skills/fabric-scripts.md +446 -0
  180. package/src/content/skills/fast-standard.md +501 -0
  181. package/src/content/skills/governance.md +197 -0
  182. package/src/content/skills/migration-assistant.md +284 -0
  183. package/src/content/skills/model-documenter.md +236 -0
  184. package/src/content/skills/pbi-connect.md +233 -0
  185. package/src/content/skills/power-query.md +398 -0
  186. package/src/content/skills/project-kickoff.md +899 -0
  187. package/src/content/skills/query-performance.md +472 -0
  188. package/src/content/skills/report-design.md +199 -0
  189. package/src/content/skills/report-layout.md +290 -0
  190. package/src/content/skills/rls-design.md +527 -0
  191. package/src/content/skills/semantic-model.md +229 -0
  192. package/src/content/skills/testing-validation.md +635 -0
  193. package/src/content/skills/theme-tweaker.md +618 -0
@@ -0,0 +1,526 @@
1
+ # Error Handling in Power Query
2
+
3
+ Patterns for handling errors, debugging, and building robust data pipelines.
4
+
5
+ ---
6
+
7
+ ## Try-Otherwise Pattern
8
+
9
+ ### Basic Error Handling
10
+
11
+ ```m
12
+ let
13
+ Source = YourSource,
14
+
15
+ // Safe transformation - returns null on error
16
+ SafeConvert = Table.TransformColumns(Source, {
17
+ {"Amount", each try Number.From(_) otherwise null, type number}
18
+ })
19
+ in
20
+ SafeConvert
21
+ ```
22
+
23
+ ### With Custom Default Value
24
+
25
+ ```m
26
+ let
27
+ Source = YourSource,
28
+
29
+ // Return 0 instead of null on error
30
+ SafeAmount = Table.TransformColumns(Source, {
31
+ {"Amount", each try Number.From(_) otherwise 0, type number}
32
+ })
33
+ in
34
+ SafeAmount
35
+ ```
36
+
37
+ ### With Error Logging
38
+
39
+ ```m
40
+ let
41
+ Source = YourSource,
42
+
43
+ // Capture both value and error status
44
+ WithErrorInfo = Table.AddColumn(Source, "ParseResult",
45
+ each
46
+ let
47
+ Result = try Number.From([Amount])
48
+ in
49
+ [
50
+ Value = if Result[HasError] then null else Result[Value],
51
+ HasError = Result[HasError],
52
+ Error = if Result[HasError] then Result[Error][Message] else null
53
+ ]
54
+ ),
55
+
56
+ // Expand the record
57
+ Expanded = Table.ExpandRecordColumn(WithErrorInfo, "ParseResult",
58
+ {"Value", "HasError", "Error"},
59
+ {"Amount_Parsed", "Amount_HasError", "Amount_Error"}
60
+ )
61
+ in
62
+ Expanded
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Try Expression Details
68
+
69
+ ### Structure of Try Result
70
+
71
+ ```m
72
+ let
73
+ // try returns a record with these fields:
74
+ // [HasError] - true/false
75
+ // [Value] - result if no error
76
+ // [Error] - error record if error occurred
77
+
78
+ Result = try Number.From("abc"),
79
+
80
+ // Result = [HasError = true, Error = [Reason = "...", Message = "...", Detail = "..."]]
81
+
82
+ SafeValue = if Result[HasError] then 0 else Result[Value]
83
+ in
84
+ SafeValue
85
+ ```
86
+
87
+ ### Access Error Details
88
+
89
+ ```m
90
+ let
91
+ Source = YourSource,
92
+
93
+ WithErrorDetails = Table.AddColumn(Source, "ErrorInfo",
94
+ each
95
+ let
96
+ Result = try SomeRiskyOperation([ColumnA])
97
+ in
98
+ if Result[HasError] then
99
+ "Error: " & Result[Error][Reason] &
100
+ " - " & Result[Error][Message]
101
+ else
102
+ "Success"
103
+ )
104
+ in
105
+ WithErrorDetails
106
+ ```
107
+
108
+ ---
109
+
110
+ ## Null Handling
111
+
112
+ ### COALESCE Pattern (First Non-Null)
113
+
114
+ ```m
115
+ let
116
+ Source = YourSource,
117
+
118
+ // Return first non-null value
119
+ WithCoalesce = Table.AddColumn(Source, "ContactEmail",
120
+ each
121
+ if [PrimaryEmail] <> null then [PrimaryEmail]
122
+ else if [SecondaryEmail] <> null then [SecondaryEmail]
123
+ else if [WorkEmail] <> null then [WorkEmail]
124
+ else "unknown@example.com",
125
+ type text
126
+ )
127
+ in
128
+ WithCoalesce
129
+ ```
130
+
131
+ ### Null-Safe Operations
132
+
133
+ ```m
134
+ let
135
+ Source = YourSource,
136
+
137
+ // Safe text operations (handle null)
138
+ SafeText = Table.TransformColumns(Source, {
139
+ {"Name", each if _ = null then "" else Text.Trim(_), type text}
140
+ }),
141
+
142
+ // Safe number operations
143
+ SafeNumber = Table.TransformColumns(Source, {
144
+ {"Amount", each if _ = null then 0 else _, type number}
145
+ })
146
+ in
147
+ SafeNumber
148
+ ```
149
+
150
+ ### Check for Null Before Operation
151
+
152
+ ```m
153
+ let
154
+ Source = YourSource,
155
+
156
+ SafeCalculation = Table.AddColumn(Source, "Result",
157
+ each
158
+ if [Denominator] = null or [Denominator] = 0 then
159
+ null
160
+ else
161
+ [Numerator] / [Denominator],
162
+ type number
163
+ )
164
+ in
165
+ SafeCalculation
166
+ ```
167
+
168
+ ---
169
+
170
+ ## Error Row Separation
171
+
172
+ ### Split Clean and Error Rows
173
+
174
+ ```m
175
+ let
176
+ Source = YourSource,
177
+
178
+ // Add error flag
179
+ WithFlag = Table.AddColumn(Source, "_HasError",
180
+ each
181
+ try (
182
+ // Validate required fields
183
+ [CustomerID] <> null and
184
+ [Amount] <> null and
185
+ Number.From([Amount]) >= 0
186
+ ) otherwise false,
187
+ type logical
188
+ ),
189
+
190
+ // Separate good and bad rows
191
+ CleanRows = Table.SelectRows(WithFlag, each [_HasError] = true),
192
+ ErrorRows = Table.SelectRows(WithFlag, each [_HasError] = false),
193
+
194
+ // Remove flag from clean data
195
+ CleanData = Table.RemoveColumns(CleanRows, {"_HasError"})
196
+ in
197
+ CleanData // or return both via Record
198
+ ```
199
+
200
+ ### Return Both Clean and Error Tables
201
+
202
+ ```m
203
+ let
204
+ Source = YourSource,
205
+
206
+ // Validation function
207
+ ValidateRow = (row as record) as logical =>
208
+ row[CustomerID] <> null and
209
+ row[Amount] <> null and
210
+ (try Number.From(row[Amount]) >= 0 otherwise false),
211
+
212
+ // Flag rows
213
+ Flagged = Table.AddColumn(Source, "_IsValid", each ValidateRow(_), type logical),
214
+
215
+ // Split
216
+ Result = [
217
+ CleanData = Table.SelectRows(Flagged, each [_IsValid] = true),
218
+ ErrorData = Table.SelectRows(Flagged, each [_IsValid] = false),
219
+ ErrorCount = Table.RowCount(Table.SelectRows(Flagged, each [_IsValid] = false)),
220
+ CleanCount = Table.RowCount(Table.SelectRows(Flagged, each [_IsValid] = true))
221
+ ]
222
+ in
223
+ Result
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Replace Errors
229
+
230
+ ### Replace All Errors in Table
231
+
232
+ ```m
233
+ let
234
+ Source = YourSource,
235
+
236
+ // Replace errors in specific columns
237
+ ReplacedErrors = Table.ReplaceErrorValues(Source, {
238
+ {"Amount", 0},
239
+ {"Quantity", 0},
240
+ {"Date", null},
241
+ {"Name", "Unknown"}
242
+ })
243
+ in
244
+ ReplacedErrors
245
+ ```
246
+
247
+ ### Replace Errors in All Columns
248
+
249
+ ```m
250
+ let
251
+ Source = YourSource,
252
+ ColumnNames = Table.ColumnNames(Source),
253
+
254
+ // Create replacement list for all columns
255
+ ReplacementList = List.Transform(
256
+ ColumnNames,
257
+ each {_, null} // Replace with null
258
+ ),
259
+
260
+ ReplacedErrors = Table.ReplaceErrorValues(Source, ReplacementList)
261
+ in
262
+ ReplacedErrors
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Validation Patterns
268
+
269
+ ### Data Type Validation
270
+
271
+ ```m
272
+ let
273
+ Source = YourSource,
274
+
275
+ // Validate data types before processing
276
+ Validated = Table.AddColumn(Source, "ValidationResult",
277
+ each
278
+ let
279
+ Checks = {
280
+ {[CustomerID] <> null, "CustomerID is required"},
281
+ {try Number.From([Amount]) <> null otherwise false, "Amount must be numeric"},
282
+ {try Date.From([OrderDate]) <> null otherwise false, "OrderDate must be valid date"},
283
+ {[Status] <> null and List.Contains({"Open", "Closed", "Pending"}, [Status]), "Invalid Status"}
284
+ },
285
+ Failures = List.Select(Checks, each not _{0}),
286
+ Messages = List.Transform(Failures, each _{1})
287
+ in
288
+ if List.Count(Messages) = 0 then "Valid"
289
+ else Text.Combine(Messages, "; ")
290
+ )
291
+ in
292
+ Validated
293
+ ```
294
+
295
+ ### Business Rule Validation
296
+
297
+ ```m
298
+ let
299
+ Source = YourSource,
300
+
301
+ ValidatedData = Table.AddColumn(Source, "BusinessRuleCheck",
302
+ each
303
+ let
304
+ Rules = {
305
+ // Rule 1: Amount must be positive
306
+ {[Amount] > 0, "Amount must be positive"},
307
+
308
+ // Rule 2: Discount cannot exceed amount
309
+ {[Discount] <= [Amount], "Discount exceeds amount"},
310
+
311
+ // Rule 3: Order date must be in the past
312
+ {[OrderDate] <= DateTime.Date(DateTime.LocalNow()), "Future order date"},
313
+
314
+ // Rule 4: Customer must exist
315
+ {[CustomerID] <> null, "Missing customer"}
316
+ },
317
+ Failures = List.Select(Rules, each not _{0}),
318
+ ErrorMessages = List.Transform(Failures, each _{1})
319
+ in
320
+ [
321
+ IsValid = List.Count(ErrorMessages) = 0,
322
+ Errors = Text.Combine(ErrorMessages, "; ")
323
+ ]
324
+ ),
325
+
326
+ // Expand validation results
327
+ Expanded = Table.ExpandRecordColumn(ValidatedData, "BusinessRuleCheck",
328
+ {"IsValid", "Errors"}
329
+ )
330
+ in
331
+ Expanded
332
+ ```
333
+
334
+ ---
335
+
336
+ ## Connection Error Handling
337
+
338
+ ### Handle Missing Data Source
339
+
340
+ ```m
341
+ let
342
+ // Try to connect, fall back gracefully
343
+ TryConnection = try Sql.Database("server", "database"),
344
+
345
+ Result =
346
+ if TryConnection[HasError] then
347
+ #table(
348
+ {"Status", "Message"},
349
+ {{"Error", "Could not connect to database: " & TryConnection[Error][Message]}}
350
+ )
351
+ else
352
+ let
353
+ Source = TryConnection[Value],
354
+ Data = Source{[Schema="dbo", Item="Sales"]}[Data]
355
+ in
356
+ Data
357
+ in
358
+ Result
359
+ ```
360
+
361
+ ### File Existence Check
362
+
363
+ ```m
364
+ let
365
+ FilePath = "C:\Data\input.csv",
366
+
367
+ // Check if file exists
368
+ FileExists = try File.Contents(FilePath) <> null otherwise false,
369
+
370
+ Result =
371
+ if not FileExists then
372
+ error Error.Record("File Not Found", "The file " & FilePath & " does not exist")
373
+ else
374
+ Csv.Document(File.Contents(FilePath), [Delimiter=","])
375
+ in
376
+ Result
377
+ ```
378
+
379
+ ---
380
+
381
+ ## Debugging Helpers
382
+
383
+ ### Add Row Numbers for Tracking
384
+
385
+ ```m
386
+ let
387
+ Source = YourSource,
388
+
389
+ // Add index for debugging
390
+ WithIndex = Table.AddIndexColumn(Source, "_RowNumber", 1, 1, Int64.Type),
391
+
392
+ // Now you can reference specific rows in error messages
393
+ Processed = Table.TransformColumns(WithIndex, {
394
+ {"Amount", each try Number.From(_) otherwise
395
+ error Error.Record("Parse Error", "Row " & Text.From([_RowNumber]) & ": Invalid amount")}
396
+ })
397
+ in
398
+ Processed
399
+ ```
400
+
401
+ ### Log Processing Steps
402
+
403
+ ```m
404
+ let
405
+ // Create log entries
406
+ Log = (message as text) => Diagnostics.Trace(TraceLevel.Information, message, () => true, true),
407
+
408
+ Source = Log("Starting query") then YourSource,
409
+ Step1 = Log("Filtering rows") then Table.SelectRows(Source, each [Status] = "Active"),
410
+ Step2 = Log("Transforming columns") then Table.TransformColumns(Step1, {{"Amount", each _ * 1.1}}),
411
+ Final = Log("Query complete, rows: " & Text.From(Table.RowCount(Step2))) then Step2
412
+ in
413
+ Final
414
+ ```
415
+
416
+ ### Conditional Debug Output
417
+
418
+ ```m
419
+ let
420
+ DebugMode = true, // Set to false in production
421
+
422
+ Source = YourSource,
423
+
424
+ // Only add debug columns in debug mode
425
+ WithDebug =
426
+ if DebugMode then
427
+ Table.AddColumn(
428
+ Table.AddColumn(Source, "_DebugRowNum", each [Index]),
429
+ "_DebugValues", each Text.From([Amount]) & "," & [Status]
430
+ )
431
+ else
432
+ Source
433
+ in
434
+ WithDebug
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Custom Error Creation
440
+
441
+ ### Raise Custom Error
442
+
443
+ ```m
444
+ let
445
+ Source = YourSource,
446
+ RowCount = Table.RowCount(Source),
447
+
448
+ // Validate before processing
449
+ Validated =
450
+ if RowCount = 0 then
451
+ error Error.Record(
452
+ "EmptySourceError",
453
+ "Source data is empty",
454
+ [RowCount = 0, Source = "YourSource"]
455
+ )
456
+ else if RowCount > 1000000 then
457
+ error Error.Record(
458
+ "DataTooLargeError",
459
+ "Source has too many rows: " & Text.From(RowCount),
460
+ [RowCount = RowCount, MaxAllowed = 1000000]
461
+ )
462
+ else
463
+ Source
464
+ in
465
+ Validated
466
+ ```
467
+
468
+ ### Error with Details
469
+
470
+ ```m
471
+ let
472
+ ValidateAmount = (amount as any) =>
473
+ let
474
+ NumericValue = try Number.From(amount)
475
+ in
476
+ if NumericValue[HasError] then
477
+ error Error.Record(
478
+ "InvalidAmountError",
479
+ "Could not parse amount: " & Text.From(amount),
480
+ [
481
+ OriginalValue = amount,
482
+ OriginalType = Value.Type(amount),
483
+ ParseError = NumericValue[Error][Message]
484
+ ]
485
+ )
486
+ else if NumericValue[Value] < 0 then
487
+ error Error.Record(
488
+ "NegativeAmountError",
489
+ "Amount cannot be negative",
490
+ [Value = NumericValue[Value]]
491
+ )
492
+ else
493
+ NumericValue[Value],
494
+
495
+ Source = YourSource,
496
+ Validated = Table.TransformColumns(Source, {
497
+ {"Amount", ValidateAmount, type number}
498
+ })
499
+ in
500
+ Validated
501
+ ```
502
+
503
+ ---
504
+
505
+ ## Best Practices Summary
506
+
507
+ ### Error Handling Checklist
508
+
509
+ | Scenario | Pattern |
510
+ |----------|---------|
511
+ | Type conversion | `try Number.From(_) otherwise null` |
512
+ | Null values | Check with `= null` before operation |
513
+ | Division | Check denominator before dividing |
514
+ | Missing columns | `try Record.Field(_, "Column") otherwise` |
515
+ | Connection errors | Wrap source in `try` block |
516
+ | Validation | Add validation column, filter errors |
517
+ | Debugging | Add row numbers, use Diagnostics.Trace |
518
+
519
+ ### Don'ts
520
+
521
+ | Bad Practice | Why |
522
+ |--------------|-----|
523
+ | Ignoring errors silently | Hides data quality issues |
524
+ | Catching too broadly | May mask real problems |
525
+ | Not logging errors | Can't diagnose issues |
526
+ | Failing on first error | Loses batch visibility |