100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Exception Handling in Pascal

Learn how try..except and try..finally let Object Pascal programs handle errors gracefully and guarantee cleanup.

Advanced PascalIntermediate9 min readJul 10, 2026
Analogies

Exception Handling in Pascal

Object Pascal (as implemented in Free Pascal and Delphi) provides structured exception handling built around the Exception class and its descendants, replacing older error-code-checking patterns like IOResult with a mechanism where an error condition is raised as an object using raise, and can be caught by a matching except block anywhere up the call stack, even several procedure calls away from where it occurred. This lets error-handling code live in one clear place instead of being scattered as if checks after every risky operation.

🏏

Cricket analogy: Raising an exception is like a fielder immediately signaling for a review the moment something looks wrong, and the decision gets handled up the chain by the third umpire, rather than every player individually having to check the replay after every single ball.

try..except and Exception Classes

A try...except block runs risky code in the try section and catches errors in the except section, where on E: ESomeException do filters for a specific exception class (and its descendants), giving access to the raised exception object E, including its .Message property. Multiple on handlers can be chained to handle different exception types differently, and a bare except ... end without any on clause catches any exception at all, though catching everything indiscriminately is usually a sign the handler is too broad.

🏏

Cricket analogy: Chaining on ERunOut do then on ECaught do handlers is like an umpire having a distinct, pre-defined protocol for a run-out review versus a catch review, rather than one vague 'something happened' response for every appeal.

pascal
type
  EInsufficientFunds = class(Exception);

procedure Withdraw(var Balance: Currency; Amount: Currency);
begin
  if Amount > Balance then
    raise EInsufficientFunds.Create('Not enough funds for withdrawal');
  Balance := Balance - Amount;
end;

var
  balance: Currency;
begin
  balance := 100.00;
  try
    Withdraw(balance, 250.00);
  except
    on E: EInsufficientFunds do
      Writeln('Withdrawal failed: ', E.Message);
    on E: Exception do
      Writeln('Unexpected error: ', E.Message);
  end;
end.

try..finally for Cleanup

While try...except catches and handles errors, try...finally guarantees that cleanup code in the finally section always runs — whether the try section completed normally, exited early, or raised an exception that propagates onward unhandled. The idiomatic pattern for any resource that must be released is Obj := TSomeClass.Create; try ... finally Obj.Free; end;, ensuring the object is freed even if an exception occurs partway through using it, and the two constructs are often combined by nesting a try...except inside a try...finally (or vice versa) when a resource needs both guaranteed cleanup and error recovery.

🏏

Cricket analogy: A try...finally block covering fielding is like a captain who always resets the field back to a standard position at the end of an over no matter what happened during it — a wicket, a six, a wide — cleanup always runs regardless of the over's outcome.

The pattern Obj := TSomeClass.Create; try ... finally Obj.Free; end; should be used for every heap-allocated object that needs guaranteed cleanup — note that Create is called before the try begins, since if Create itself fails there is no object to free, and putting Create inside the try would risk calling Free on an object that was never successfully constructed.

Custom Exceptions and Best Practices

Defining a custom exception is as simple as declaring a class descending from Exception (or a more specific existing exception class), such as EInsufficientFunds = class(Exception);, which inherits the Create(Msg: string) constructor and .Message property automatically. Good practice is to catch the most specific exception types you can meaningfully recover from, let unrelated exceptions propagate up rather than swallowing them silently, and never leave an empty except end block, which hides real bugs by discarding error information entirely.

🏏

Cricket analogy: Defining EInsufficientFunds as a specific exception class is like a scoring app having a distinct 'NoBallException' rather than lumping every irregular delivery under one vague 'BadBall' flag — specificity lets each situation be handled correctly.

An empty except end; block (or one that only does except on E: Exception do ; end;) silently discards the exception entirely — the program continues as if nothing went wrong while the actual cause of the failure is lost forever. At minimum, log E.Message and the exception class name before deciding to suppress an error, and never suppress exceptions you don't specifically expect and know how to recover from.

  • Exceptions are raised with raise SomeException.Create('message') and caught with try...except.
  • on E: ESpecificException do filters for a specific exception class and exposes E.Message.
  • Multiple on handlers can be chained to react differently to different exception types.
  • try...finally guarantees cleanup code always runs, whether or not an exception occurred.
  • The Create-then-try-finally-Free pattern ensures objects are freed even when errors occur mid-use.
  • Custom exceptions are declared by descending from Exception or a more specific existing class.
  • Never leave an empty except block — always log or meaningfully handle the caught error.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#PascalStudyNotes#ExceptionHandlingInPascal#Exception#Handling#Pascal#Try#ErrorHandling#StudyNotes#SkillVeris