1. Error Handling

Error handling is a fundamental aspect of writing robust PostScript programs. This guide provides an overview of error handling strategies and best practices suitable for all skill levels.

1.1. Overview

Error handling in PostScript involves:

  • Understanding errors - Types and causes

  • Detecting errors - Catching and identifying issues

  • Recovering from errors - Graceful degradation

  • Preventing errors - Defensive programming

  • Reporting errors - Clear diagnostics

Good error handling makes programs more reliable and easier to debug.

1.2. Common Error Types

1.2.1. Operational Errors

Errors that occur during normal operation:

% stackunderflow - not enough operands
pop  % ERROR: empty stack

% typecheck - wrong type
5 (text) add  % ERROR: incompatible types

% rangecheck - value out of range
-5 array  % ERROR: negative size

% undefinedresult - invalid operation
1 0 div  % ERROR: division by zero

1.2.2. Resource Errors

Errors related to system resources:

% VMerror - out of memory
99999999 array

% ioerror - file operation failed
(nonexistent.ps) (r) file

% limitcheck - implementation limit exceeded
% (varies by interpreter)

1.2.3. Syntax Errors

Errors in program structure:

% syntaxerror - invalid PostScript syntax
{ ( unclosed string

% unmatchedmark - mismatched brackets
5 10 ]  % ERROR: no matching [

1.3. Basic Error Handling

1.3.1. The stopped Operator

Primary error catching mechanism:

% stopped: mark proc stopped -> true | false

{
  % Code that might error
  1 0 div
} stopped {
  % Error occurred
  (Error: Division by zero) print
} {
  % Success
  (Division succeeded) print
} ifelse

1.3.2. Simple Error Recovery

/safeDivide {  % a b -> result
  dup 0 eq {
    % Handle error condition
    pop pop
    (Cannot divide by zero) print
    0  % Return safe default
  } {
    div
  } ifelse
} def

% Usage
10 5 safeDivide =   % 2
10 0 safeDivide =   % 0 (safe default)

1.3.3. Error Messages

{
  undefined_name
} stopped {
  (An error occurred) print

  % Get error details from $error dictionary
  $error /errorname get (Error type: ) exch =string cvs concatstrings print
  $error /command get (Failed command: ) exch =string cvs concatstrings print
} if

1.4. Error Prevention

1.4.1. Input Validation

/divide {  % a b -> result
  % Validate input
  dup 0 eq {
    pop pop
    (Error: divisor cannot be zero) print
    /divisionbyzero cvx
  } {
    div
  } ifelse
} def

1.4.2. Type Checking

/addNumbers {  % a b -> sum
  % Check first operand
  dup type /integertype ne {
    (Error: first arg must be integer) print
    stop
  } if

  % Check second operand
  1 index type /integertype ne {
    (Error: second arg must be integer) print
    stop
  } if

  add
} def

1.4.3. Bounds Checking

/getElement {  % array index -> element
  2 copy
  length ge {
    (Error: index out of bounds) print
    stop
  } if
  get
} def

1.5. Error Reporting

1.5.1. Informative Messages

/reportError {  % context message -> -
  (ERROR in ) print
  exch print
  (: ) print
  print
  () print
} def

% Usage
{
  1 0 div
} stopped {
  (divide operation) (Division by zero) reportError
} if

1.5.2. Error Context

/withContext {  % contextName proc -> -
  exch /currentContext exch def

  {
    exec
  } stopped {
    (Error in context: ) print currentContext print
    () print
  } if
} def

% Usage
(file processing) {
  (nonexistent.ps) run
} withContext

1.6. Practical Error Handling

1.6.1. File Operations

/safeFileRead {  % filename -> content true | false
  {
    (r) file
    dup 1024 string exch readstring pop
    exch closefile
    true
  } stopped {
    pop
    (Could not read file) print
    false
  } ifelse
} def

% Usage
(data.txt) safeFileRead {
  (File contents: ) print print
} {
  (Using default data) print
} ifelse

1.6.2. Array Operations

/safeArrayAccess {  % array index -> element true | false
  {
    get
    true
  } stopped {
    pop pop
    false
  } ifelse
} def

% Usage
[10 20 30] 5 safeArrayAccess {
  =  % Got element
} {
  (Index out of range) print
} ifelse

1.6.3. Calculation Safety

/safeCalculate {  % proc -> result true | errorMessage false
  {
    exec
    true
  } stopped {
    $error /errorname get
    =string cvs
    ( error occurred) concatstrings
    false
  } ifelse
} def

% Usage
{ 10 5 div } safeCalculate {
  (Result: ) print =
} {
  (Calculation failed: ) print print
} ifelse

1.7. Error Handling Patterns

1.7.1. Try-Catch Pattern

/try {  % tryProc catchProc -> -
  exch
  {
    exec
  } stopped {
    exec
  } if
  pop
} def

% Usage
{
  % Try this
  1 0 div
} {
  % Catch errors here
  (Caught division error) print
} try

1.7.2. Default Value Pattern

/getOrDefault {  % proc defaultValue -> result
  exch
  {
    exec
  } stopped {
    % Return default on error
  } if
} def

% Usage
{ nonexistent_variable } 42 getOrDefault
% Returns 42 if variable doesn't exist

1.7.3. Retry Pattern

/retry {  % proc maxAttempts -> result
  1 dict begin
    /attempts exch def
    /proc exch def

    1 1 attempts {
      /attempt exch def

      {
        proc exec
        exit  % Success
      } stopped {
        attempt attempts lt {
          (Retry attempt ) print attempt 1 add =
        } {
          (All attempts failed) print
          stop
        } ifelse
      } ifelse
    } for
  end
} def

1.8. Best Practices

1.8.1. Always Handle Errors

% Good: error handled
{
  risky_operation
} stopped {
  (Operation failed, using fallback) print
} if

% Bad: error ignored
risky_operation  % Might crash program

1.8.2. Provide Clear Messages

% Good: descriptive error
dup 0 eq {
  (Error: Cannot divide ) print
  2 index =string cvs print
  ( by zero) print
  stop
} if

% Bad: generic error
dup 0 eq { stop } if

1.8.3. Clean Up Resources

% Good: cleanup on error
(file.txt) (r) file /f exch def
{
  f 256 string readline pop
} stopped {
  f closefile
  stop
} {
  f closefile
} ifelse

% Bad: file left open on error
(file.txt) (r) file /f exch def
f 256 string readline pop

1.8.4. Use Appropriate Error Levels

% Critical errors: stop execution
dup 0 eq { stop } if

% Warnings: log but continue
count 10 gt {
  (Warning: Deep stack) print
} if

% Info: normal operation feedback
(Processing item ) print i =

1.9. Testing Error Handling

1.9.1. Test Error Paths

% Test success case
{ 10 5 divide } safeCalculate pop

% Test error case
{ 10 0 divide } safeCalculate not {
  (Error correctly handled) print
} {
  (ERROR: Should have failed!) print
} ifelse

1.9.2. Verify Error Messages

{
  undefined_name
} stopped {
  $error /errorname get /undefined eq {
    (Correct error type) print
  } {
    (Wrong error type!) print
  } ifelse
} if

1.10. Common Pitfalls

1.10.1. Swallowing Errors

% Wrong: hides problems
{ operation } stopped pop

% Correct: log or handle
{ operation } stopped {
  (Error occurred) print
} if

1.10.2. Not Checking Return Values

% Wrong: assumes success
file string readline pop

% Correct: check boolean
file string readline {
  % Success - got line
} {
  % EOF or error
} ifelse

1.10.3. Incomplete Error Handling

% Wrong: only handles one error
{ operation } stopped {
  $error /errorname get /ioerror eq {
    (IO error) print
  } if
  % Other errors ignored!
} if

% Correct: handle all cases
{ operation } stopped {
  $error /errorname get
  dup /ioerror eq {
    (IO error) print
  } {
    (Other error: ) print =
  } ifelse
} if

1.11. Quick Reference

1.11.1. Error Types

% Common errors:
% - stackunderflow: not enough values
% - typecheck: wrong type
% - rangecheck: out of range
% - undefined: name not found
% - VMerror: out of memory
% - ioerror: file error
% - syntaxerror: syntax error

1.11.2. Basic Template

{
  % Risky operation
  operation
} stopped {
  % Error handling
  (Error: ) print
  $error /errorname get =string cvs print
} if

1.11.3. Error Information

% Get error details:
$error /errorname get  % Error type
$error /command get    % Failed command
$error /newerror get   % Is new error?

1.12. Next Steps

For more detailed error handling:

1.13. See Also


Back to top

Copyright © 2025 Ribose. PostScript is a trademark of Adobe. Distributed under the MIT License.