Limitations and Differences from Standard AL
ToolBox AL is a runtime-interpreted subset of Business Central AL. Most everyday code runs unchanged, but a number of language features are deliberately not part of the interpreter, and a few behave differently. This page collects them in one place so you can check a script before running it.
Program Shape
- A script is a single unit: an optional
varblock (or several), optional procedures, and one
main begin ... end; block. There are no object declarations (codeunit 50100 ...), no trigger OnRun(), and no attributes such as [TryFunction] or [EventSubscriber].
- Procedures cannot have a
Record,Codeunit, or array return type. Return such values
through a var parameter instead.
exit(value)is only valid inside a procedure that declares a return type. In the main block,
exit; ends the script.
Keywords and Identifiers
- Identifiers, keywords, table names, and field names are case-insensitive.
- The reserved words are: `and or xor not div mod break else false true for foreach to downto
procedure trigger if then exit var while do repeat until begin end case of page codeunit report run runmodal action. To use one as a name, quote it: "Report"`.
inandtemporaryare not reserved; they are recognized by position.
Types That Are Not Available
The following standard AL types cannot be declared in a ToolBox script. Where a replacement exists it is listed.
| Not available | Use instead |
|---|---|
TextBuilder |
Text with += (see Text Methods) |
Label, TextConst |
Text literals or Text variables |
Enum types and Enum::Name::Value syntax |
Rec.Field::Member or the integer ordinal |
Interface |
-- |
Variant |
-- |
RecordRef, FieldRef, KeyRef, RecordId |
Record variables; Rec.RecordId() returns the formatted id as Text |
HttpClient, HttpRequestMessage, HttpResponseMessage, HttpHeaders, HttpContent |
Codeunit "Rest Client" |
XmlDocument and the other Xml types |
-- |
Query, Page, Report, XmlPort variables |
Page.Run(Id), Report.Run(Id) statements |
Blob, BigText, Media, MediaSet variables |
Codeunit "Temp Blob" with streams (Blob fields open streams directly) |
DotNet, SecretText, ErrorInfo, Notification, SessionSettings, ModuleInfo, TestPage |
-- |
| Multi-dimensional arrays | One-dimensional arrays or a List |
Nested collections such as List of [List of [Text]] |
A Dictionary or a JSON structure |
| Codeunit subtypes other than the supported list | Codeunit.Run(Id[, Rec]) |
Statements and Expressions
- Precedence follows standard AL, not C.
andandxorbind like*,orbinds like+,
and comparisons bind loosest. Always parenthesize each comparison inside a logical expression: if (Qty > 0) and (Price > 0) then. Writing if Qty > 0 and Price > 0 then is a type error.
andandordo not short-circuit. Both operands are always evaluated, so the left side
cannot guard the right side. Use nested if statements when the right side may fail.
notapplies only to the primary expression that follows it: writenot (X in [1, 2]).foreachruns over aList, aJsonArray(with aJsonTokenloop variable), or an expression
that returns a list. It does not run over a Dictionary (use Keys() or Values()) or over an array (use an indexed for loop).
- Whole-array assignment (
A := B) is a compile error, as in standard AL. UseCopyArray. with ... do,asserterror, labels/goto, and event subscriptions are not available.- There is no try/catch.
Error()stops the script;GetLastErrorText,ClearLastError, and
[TryFunction] do not exist.
Literals
- Only
0DTis a validDateTimeliteral. Build other values with
CreateDateTime(20240131D, 120000T).
- Text literals use single quotes with
''as the only escape. A literal cannot span lines.
To embed a line break, assign character code 10 through string indexing: NL[1] := 10;.
- Numbers have no hexadecimal, exponent, or thousands-separator forms.
Text and Code
- Text methods (
ToLower,Split,IndexOf, ...) require aTextvariable as the receiver, or
the Text result of a chained call. They are not available on Code variables, string literals, or record fields directly.
- Positions in text methods are 1-based and
IndexOfreturns 0 when nothing is found, matching
native AL.
- Assigning a value that is longer than a
Text[N]orCode[N]variable or field raises an
overflow error. Use CopyStr(Value, 1, MaxStrLen(Target)).
- A
Decimalwith a fractional part cannot be stored in anInteger; round it first.
Records
- Fields of type Blob, Media, MediaSet, RecordId, and TableFilter cannot be read or assigned as
values. Blob fields open streams (Rec.BlobField.CreateOutStream/CreateInStream/HasValue/ Length); Import/Export are unavailable. Codeunit "Temp Blob" also moves Blob content with FromRecordRef and ToRecordRef.
Renameis not supported. The following record members are also unavailable:SetLoadFields,
SetAutoCalcFields, SetView, GetView, GetPosition, SetPosition, FieldNo, CurrentKey, AddLink, CopyFilters, CopyLinks, SetPermissionFilter, ReadIsolation, SetSecurityFilter, Consistent.
Getused as a statement raises an error when the record is not found; used in an expression
(if Rec.Get(...) then) it returns Boolean. Insert, Modify, and Delete behave the same way.
- Option and enum fields are compared and assigned with
Rec.Field::Memberor an integer. There
is no Enum::Type::Value syntax.
- Writes to database tables require write access on the ToolBox license and may ask the user for
consent per table. Temporary records never do.
- A record routed to another environment with
ChangeEnvironmentsupports only reading:
Reset, SetRange, SetFilter, GetFilter(s), SetCurrentKey, Ascending, CalcFields, FindSet, Find, FindFirst, FindLast, Next, Get, and Count.
User Interaction
Message output goes to the console. Confirm, StrMenu, Dialog, UploadIntoStream, DownloadFromStream, Page.Run, Page.RunModal, and exopen need a user session and fail in Job Queue runs. Test GuiAllowed before calling them in a program that also runs in the background.
Extensions ToolBox Adds to Standard AL
These are conveniences that do not exist in standard AL:
- The ternary operator
Cond ? A : B. - Multi-line comments
/* ... */alongside//. Rest ClientmembersGetandDeletereturn the response body asText, and the*AsJson
members return a JsonToken.
JsonObjectandJsonArraytyped getters with aDefaultIfNotFoundoverload
(GetText(Key, true)).
Format Addressmembers have a text-only form returningList of [Text].InStream.ReadText()without arguments returns the line, andOutStream.WriteText()without
arguments writes a line terminator.
ModifyAlltakesUseValidateas its third argument.Rec.ChangeEnvironment(...)reads data from another environment.- The Excel functions
exget,exset,exclear,exopenand theeval(Source)function that
runs a script fragment and returns its console output.