3.19 Runtime Errors

A runtime error is a special condition encountered during execution of the filter program, that makes further execution of the program impossible. There are two kinds of runtime errors: fatal errors, and uncaught exceptions. Whenever a runtime error occurs, mailfromd writes into the log file the following message:

 
RUNTIME ERROR near file:line: text

where file:line indicates approximate source file location where the error occurred and text gives the textual description of the error.

Fatal runtime errors

Fatal runtime errors are caused by a condition that is impossible to fix at run time. For version 6.0 these are:

Not enough memory

There is not enough memory for the execution of the program. Try to make more memory available for mailfromd or to reduce its memory requirements by rewriting your filter script.

Out of stack space; increase #pragma stacksize
Heap overrun; increase #pragma stacksize
memory chunk too big to fit into heap

These errors are reported when there is not enough space left on stack to perform the requested operation, and the attempt to resize the stack has failed. Usually mailfromd expands the stack when the need arises (see automatic stack resizing). This runtime error indicates that there were no more memory available for stack expansion. Try to make more memory available for mailfromd or to reduce its memory requirements by rewriting your filter script.

Stack underflow

Program attempted to pop a value off the stack but the stack was already empty. This indicates an internal error in the MFL compiler or mailfromd runtime engine. If you ever encounter this error, please report it to bug-mailfromd@gnu.org.ua. Include the log fragment (about 10-15 lines before and after this log message) and your filter script. See section How to Report a Bug, for more information about bug reporting.

pc out of range

The program counter is out of allowed range. This is a severe error, indicating an internal inconsistency in mailfromd runtime engine. If you encounter it, please report it to bug-mailfromd@gnu.org.ua. Include the log fragment (about 10-15 lines before and after this log message) and your filter script. See section How to Report a Bug, for more information about how to report a bug.

Programmatic runtime errors

These indicate a programmatic error in your filter script, which the MFL compiler was unable to discover at compilation stage:

Invalid exception number: n

The throw statement used a not existent exception number n. Fix the statement and restart mailfromd. See throw, for the information about throw statement and see Exceptional Conditions, for the list of available exception codes.

No previous regular expression

You have used a back-reference (see section Back references), where there is no previous regular expression to refer to. Fix this line in your code and restart the program.

Invalid back-reference number

You have used a back-reference (see section Back references), with a number greater than the number of available groups in the previous regular expression. For example:

 
  if $f matches "(.*)@gnu.org"
    # Wrong: there is only one group in the regexp above!
    set x \2
  …

Fix your code and restart the daemon.

Uncaught exceptions

Another kind of runtime errors are uncaught exceptions, i.e. exceptional conditions for which no handler was installed (See section Exceptional Conditions, for information on exceptions and on how to handle them). These errors mean that the programmer (i.e. you), made no provision for some specific conditions. For example, consider the following code:

 
prog envfrom
do
  if $f mx matches "yahoo.com"
    foo()
  fi
done

It is syntactically correct, but it overlooks the fact that mx matches may generate e_temp_failure exception, if the underlying DNS query has timed out (see section Special Comparisons). If this happens, mailfromd has no instructions on what to do next and reports an error. This can easily be fixed using a catch statement, e.g.:

 
prog envfrom
do
  # Catch DNS errors
  catch e_temp_failure or e_failure
  do
    tempfail 451 4.1.1 "MX verification failed"
  done
  
  if $f mx matches "yahoo.com"
    foo()
  fi
done

Another common case are undefined Sendmail macros. In this case the e_macroundef exception is generated:

 
RUNTIME ERROR near foo.c:34: Macro not defined: {client_adr}

These can be caused either by misspelling the macro name (as in the example message above) or by failing to export the required name in Sendmail milter configuration (see exporting macros). This error should be fixed either in your source code or in ‘sendmail.cf’ file, but if you wish to provide a special handling for it, you can use the following catch statement:

 
catch e_macroundef
do
  …
done  

Sometimes the location indicated with the runtime error message is not enough to trace the origin of the error. For example, an error can be generated explicitly with throw statement (see throw):

 
RUNTIME ERROR near match_cidr.mf:30: invalid CIDR (text)

If you look in module ‘match_cidr.mf’, you will see the following code (line numbers added for reference):

 
23 func match_cidr(string ipstr, string cidr) returns number
24 do
25   number netmask
26
27   if %cidr matches '^(([0-9]{1,3}\.){3}[0-9]{1,3})/([0-9][0-9]?)'
28     return inet_aton(%ipstr) & len_to_netmask(\3) = inet_aton(\1)
29   else
30     throw invcidr "invalid CIDR (%cidr)"
31   fi
32   return 0
33 done

Now, it is obvious that the value of cidr argument to match_cidr was wrong, but how to find the caller that passed the wrong value to it? The special command line option ‘--stack-trace’ is provided for this. This option enables dumping stack traces when a fatal error occurs. The traces contain information about function calls. Continuing our example, using the ‘--stack-trace’ option you will see the following diagnostics:

 
RUNTIME ERROR near match_cidr.mf:30: invalid CIDR (127%)
mailfromd: Stack trace:
mailfromd: 0077: match_cidr.mf:30: match_cidr
mailfromd: 0096: test.mf:13: bar
mailfromd: 0110: mailfromd.mf:18: foo
mailfromd: Stack trace finishes
mailfromd: Execution of the configuration program was not finished

Each trace line describes one stack frame. The lines appear in the order of most recently called to least recently called. Each frame consists of:

  1. Value of program counter at the time of its execution
  2. Source code location, if available
  3. Name of the function called

Thus, the example above can be read as: “the function match_cidr was called by function bar in file ‘test.mf’ at line 13. This function was called from function bar, in file ‘test.mf’ at line 13. In its turn, bar was called by function foo, in file ‘mailfromd.mf’ at line 18”.

Examining the caller functions will help you localize the source of the error and fix it.

You can also request a stack trace any place in your code, by calling the stack_trace function. This can be useful for debugging, or in your catch statements.