Add lifecycle error and privacy knowledge (#99)

* Add lifecycle error and privacy knowledge

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

Copilot-Session: 95c06ad8-377d-4faa-8d07-06300b1c81ec

* Fix lifecycle privacy review findings

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: a26cd6d6-ff49-433e-bc53-f645c455ebdd

* Refine lifecycle privacy retrieval

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

Copilot-Session: 95c06ad8-377d-4faa-8d07-06300b1c81ec

* Make review gates explicit

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

Copilot-Session: 95c06ad8-377d-4faa-8d07-06300b1c81ec

---------

Co-authored-by: Jesper Schulz-Wedde <jesper.schulzwedde@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Jesper Schulz-Wedde 2026-07-14 12:52:56 +02:00 committed by GitHub
parent 363f08f47e
commit e0ebdd35c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 459 additions and 39 deletions

View file

@ -7,7 +7,6 @@ codeunit 50190 "Error Type Good Sample"
if not BucketInitialized(BucketId) then begin
InternalErr.ErrorType := ErrorType::Internal;
InternalErr.Message := StrSubstNo('Ledger bucket %1 was not initialized before posting.', BucketId);
InternalErr.DetailedMessage := 'Internal invariant violated. Inspect the call stack captured in telemetry.';
Error(InternalErr);
end;
end;

View file

@ -1,5 +1,5 @@
---
bc-version: [all]
bc-version: [14..]
domain: error-handling
keywords: [errorinfo, errortype, internal, client, telemetry, diagnostics, generic-message]
technologies: [al]
@ -15,7 +15,7 @@ application-area: [all]
## Best Practice
Reserve `ErrorType::Internal` for errors the user cannot act on: corrupted internal state, an unreachable branch, a contract a caller violated. Set a precise, detail-rich `Message` and `DetailedMessage` for telemetry, raise it via `Error(ErrorInfo)`, and let the platform show the user a generic dialog. Keep `ErrorType::Client` (or a plain `Error`) for failures the user is expected to read and resolve — validation messages, missing setup, business-rule violations. The test is simple: if the message only makes sense to a developer, mark it `Internal`.
Reserve `ErrorType::Internal` for errors the user cannot act on: corrupted internal state, an unreachable branch, a contract a caller violated. Set a precise, detail-rich `Message` for telemetry, raise it via `Error(ErrorInfo)`, and let the platform show the user a generic dialog. Keep `ErrorType::Client` (or a plain `Error`) for failures the user is expected to read and resolve — validation messages, missing setup, business-rule violations. The test is simple: if the message only makes sense to a developer, mark it `Internal`.
See sample: `errortype-internal-vs-client-for-diagnostics.good.al`.

View file

@ -0,0 +1,17 @@
codeunit 50301 "Try Return Bad"
{
procedure ImportDocument()
begin
// Ignoring the Boolean result makes this an ordinary, throwing call.
TryImportDocument();
end;
[TryFunction]
local procedure TryImportDocument()
begin
Error(SourceRejectedErr);
end;
var
SourceRejectedErr: Label 'The source document was rejected.';
}

View file

@ -0,0 +1,18 @@
codeunit 50300 "Try Return Good"
{
procedure ImportDocument()
begin
if not TryImportDocument() then
Error(ImportFailedErr);
end;
[TryFunction]
local procedure TryImportDocument()
begin
Error(SourceRejectedErr);
end;
var
ImportFailedErr: Label 'The document could not be imported.';
SourceRejectedErr: Label 'The source document was rejected.';
}

View file

@ -0,0 +1,26 @@
---
bc-version: [13..]
domain: error-handling
keywords: [tryfunction, try-method, boolean-return, ignored-return-value, error-propagation]
technologies: [al]
countries: [w1]
application-area: [all]
---
# Consume a TryFunction return value to enable try semantics
## Description
A procedure marked `[TryFunction]` catches errors only when the caller uses its Boolean return value. An assignment or conditional makes the invocation a try-method call; a bare call is treated as an ordinary procedure call and exposes errors as usual. The attribute alone does not make every invocation non-throwing.
## Best Practice
Consume the result directly: assign it to a Boolean or use the call in an `if` condition. Handle `false` immediately while the last-error state still describes that failure.
See sample: `ignored-tryfunction-return-disables-try-semantics.good.al`.
## Anti Pattern
Calling a `[TryFunction]` procedure as a standalone statement and assuming the attribute suppresses its errors. The call has ordinary error semantics because its Boolean result is ignored.
See sample: `ignored-tryfunction-return-disables-try-semantics.bad.al`.