Skip to main content

Common Errors

Access Violation when calling TOption<T>.Value

Cause: Value (and Unwrap) raise Exception.Create('Attempted to access a value from TOption.None') when the option is empty.

Fix: Always guard with IsSome or use a safe extractor:

// Bad — may raise
WriteLn(LName.Value);

// Good
if LName.IsSome then
WriteLn(LName.Value);

// Better — safe default
WriteLn(LName.UnwrapOr('(no name)'));

TMatch<T>.Execute returns IsFailure with "Use Execute after session..."

Cause: Execute was called before any case builder (CaseEq, CaseGt, etc.) or Default. The match session is still in sMatch state.

Fix: Add at least one case or a Default before calling Execute:

// Bad
TMatch<Integer>.Value(LValue).Execute;

// Good
TMatch<Integer>.Value(LValue)
.CaseGt(0, procedure begin WriteLn('Positive'); end)
.Default(procedure begin WriteLn('Zero or negative'); end)
.Execute;

TMatch<T>.Execute returns IsFailure with "No matching case found."

Cause: The input value did not match any registered case and no Default was provided.

Fix: Always add a Default at the end of each match:

TMatch<string>.Value(LStatus)
.CaseEq('active', procedure begin { ... } end)
.CaseEq('inactive', procedure begin { ... } end)
.Default(procedure begin WriteLn('Unknown: ' + LStatus); end) // required
.Execute;

TResultPair<S,F> — accessing ValueSuccess on a failure result

Cause: ValueSuccess on a Failure result is implementation-defined (may return default or raise).

Fix: Always check IsSuccess or IsFailure first:

var LResult: TResultPair<Integer, string>;
begin
LResult := ParseInt('abc');
// Bad
WriteLn(LResult.ValueSuccess); // undefined if failure

// Good
if LResult.IsSuccess then
WriteLn(LResult.ValueSuccess)
else
WriteLn('Error: ', LResult.ValueFailure);
end;

TSafeTry.End returns IsErr even though no exception was raised

Cause: The Try(AProc) overload with nil will do nothing. Ensure a non-nil proc or func is passed.

Fix:

// Bad — nil proc
LResult := TSafeTry.Try(nil).End;

// Good
LResult := TSafeTry.Try(procedure begin DoWork; end).End;

TAsync.Await hangs indefinitely

Cause: The background task never completes, or a timeout was not set.

Fix: Pass an explicit timeout in milliseconds:

LFuture := Async(procedure begin LongWork; end).Await(5000); // 5 s timeout
if LFuture.IsErr then
WriteLn('Timed out or failed: ', LFuture.Err);

Flatten<U> raises "Flatten só pode ser usado quando T é TOption<U>"

Cause: Flatten<U> is only valid when T is itself a TOption<U>. The inner type must be accessible via RTTI.

Fix: Use Flatten<U> only on TOption<TOption<U>>:

var
LNested: TOption<TOption<string>>;
LFlat : TOption<string>;
begin
LNested := TOption<TOption<string>>.Some(TOption<string>.Some('hello'));
LFlat := LNested.Flatten<string>;
end;

Linux64 compile error: OutputDebugString not found

Cause: The Linux target does not have Windows unit; OutputDebugString is Windows-only.

Fix: This is already handled by {$IFDEF MSWINDOWS} guards in ModernSyntax source. If you see this error, ensure you are using the version from the official repository and not a patched or partial copy.


TDotEnv — key not found, returns empty TValue

Cause: Either the key is missing from the .env file or AUseSystemFallback is False and the key is not in the OS environment.

Fix:

var LEnv: TDotEnv;
begin
LEnv := TDotEnv.Create('.env', True); // fallback to OS env
try
var LValue := LEnv['DATABASE_URL'];
if LValue.IsEmpty then
raise Exception.Create('DATABASE_URL is not set');
finally
LEnv.Free;
end;
end;