Address guidance review findings

Gate SecretText guidance to BC23 and clarify that the collectible-error sample intentionally emits a message-only blocking aggregate.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: e05a43e7-6448-4d67-9c73-798523f5d945
This commit is contained in:
Jesper Schulz-Wedde 2026-07-14 11:18:50 +02:00
parent bc8686d871
commit 97d19631ce
3 changed files with 7 additions and 6 deletions

View file

@ -1,5 +1,5 @@
--- ---
bc-version: [all] bc-version: [23..]
domain: breaking-changes domain: breaking-changes
keywords: [sensitive-data, secrettext, token, credential, public-api, access-boundary] keywords: [sensitive-data, secrettext, token, credential, public-api, access-boundary]
technologies: [al] technologies: [al]

View file

@ -17,6 +17,7 @@ codeunit 50185 "Collect Errors Good Sample"
if HasCollectedErrors() then begin if HasCollectedErrors() then begin
// The default is false; true retrieves and clears the collection. // The default is false; true retrieves and clears the collection.
CollectedErrors := GetCollectedErrors(true); CollectedErrors := GetCollectedErrors(true);
// This blocking aggregate intentionally retains messages only.
foreach CollectedError in CollectedErrors do foreach CollectedError in CollectedErrors do
ErrorText += CollectedError.Message() + '\'; ErrorText += CollectedError.Message() + '\';
Error(ErrorInfo.Create( Error(ErrorInfo.Create(
@ -33,9 +34,9 @@ codeunit 50186 "Collect Errors Item Check"
begin begin
if Rec.Description = '' then if Rec.Description = '' then
Error(ErrorInfo.Create( Error(ErrorInfo.Create(
StrSubstNo('Item %1 has no description.', Rec."No."), true, Rec, Rec.FieldNo(Description))); StrSubstNo('Item %1 has no description.', Rec."No."), true));
if Rec."Unit Cost" <= 0 then if Rec."Unit Cost" <= 0 then
Error(ErrorInfo.Create( Error(ErrorInfo.Create(
StrSubstNo('Item %1 must have a positive unit cost.', Rec."No."), true, Rec, Rec.FieldNo("Unit Cost"))); StrSubstNo('Item %1 must have a positive unit cost.', Rec."No."), true));
end; end;
} }

View file

@ -11,16 +11,16 @@ application-area: [all]
## Description ## Description
By default a procedure stops on the first `Error`, so a user fixing ten bad rows must rerun the operation ten times. The collectible-errors feature postpones error handling to the end of the call: a procedure attributed `[ErrorBehavior(ErrorBehavior::Collect)]` keeps running as errors occur and gathers them, so all failures can be presented together. `GetCollectedErrors()` returns a `List of [ErrorInfo]` but does not clear the collection by default; pass `true` to retrieve and clear in one call, or call `ClearCollectedErrors()` explicitly after retrieving. This is a platform mechanism most LLMs are unaware of — they reach for a manually concatenated `Text` buffer or a temporary error table instead. By default a procedure stops on the first `Error`, so a user fixing ten bad rows must rerun the operation ten times. The collectible-errors feature postpones error handling to the end of the call: a procedure attributed `[ErrorBehavior(ErrorBehavior::Collect)]` keeps running as collectible errors occur and gathers them, so all failures can be presented together. `GetCollectedErrors()` returns a `List of [ErrorInfo]` for the handler to inspect, but does not clear the collection by default; pass `true` to retrieve and clear in one call, or call `ClearCollectedErrors()` explicitly after retrieving. A handler can copy record information into a custom error page as Microsoft Learn demonstrates, or deliberately format only the messages into a final blocking error as this article's sample does.
## Best Practice ## Best Practice
Mark the orchestrating procedure `[ErrorBehavior(ErrorBehavior::Collect)]` and run each item's validation so one failure doesn't abandon the rest — typically by calling the per-item routine through `Codeunit.Run`. When the run finishes, inspect `HasCollectedErrors()`, retrieve and clear the list with `GetCollectedErrors(true)`, and fail the operation with the collected validation details. Do not replace validation failure with `Message`: clearing collected errors suppresses the platform failure, so the custom handler must still block the invalid operation. Mark the orchestrating procedure `[ErrorBehavior(ErrorBehavior::Collect)]` and run each item's validation so one failure doesn't abandon the rest — typically by calling the per-item routine through `Codeunit.Run`. When the run finishes, inspect `HasCollectedErrors()`, retrieve and clear the list with `GetCollectedErrors(true)`, and fail the operation with the collected messages. The sample intentionally produces a text aggregate and does not claim to retain record/field metadata in the final error. If that metadata is needed, map each `ErrorInfo` to a custom error UI before clearing, following the Microsoft Learn pattern. Do not replace validation failure with `Message`: clearing collected errors suppresses the platform failure, so the custom handler must still block the invalid operation.
See sample: `collect-validation-errors-with-errorbehavior.good.al`. See sample: `collect-validation-errors-with-errorbehavior.good.al`.
## Anti Pattern ## Anti Pattern
Three shapes signal trouble. Hand-rolled accumulation reimplements the platform feature and loses each error's `ErrorInfo` structure. A `Collect` procedure that never handles the collection falls back to the concatenated platform dialog. Finally, code that calls parameterless `GetCollectedErrors()`, assumes it cleared the list, and only shows a `Message` can both leave the errors collected and allow invalid processing to continue. Three shapes signal trouble. Hand-rolled accumulation reimplements collection and prevents the handler from receiving individual `ErrorInfo` values. A `Collect` procedure that never handles the collection falls back to the concatenated platform dialog. Finally, code that calls parameterless `GetCollectedErrors()`, assumes it cleared the list, and only shows a `Message` can both leave the errors collected and allow invalid processing to continue.
See sample: `collect-validation-errors-with-errorbehavior.bad.al`. See sample: `collect-validation-errors-with-errorbehavior.bad.al`.