Errors, Warnings and Exceptions

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

Written against MATLAB R2026b. The error and warning machinery described here is old and stable: MException and rethrow were introduced in R2007b and onCleanup in R2008a, and the only recent addition is the cancel method of onCleanup in R2025a. The release numbers come from the "Introduced in" notes of the R2026b reference pages.

GNU Octave 11.3.0 is not MATLAB here, and the differences matter on this page. Octave has error, warning, lastwarn, assert, rethrow, try/catch and onCleanup, so the examples that use only those were run, and their output is labelled "Octave 11.3.0 output". Octave does not implement MException, throw, throwAsCaller, addCause or getReport (calling MException stops with a message that the function is not yet implemented in Octave), and the variable of catch err is a plain structure, not an object. Everything that depends on MException, on MATLAB’s own error identifiers or on the cancel method is labelled "not run here" and checked line by line against the R2026b documentation. Octave’s own identifiers and messages differ from MATLAB’s and are shown only to make that point.

Errors, warnings and exceptions

MATLAB separates two kinds of problem report. The documentation defines the difference in two points. A warning flags an unexpected condition, prints a message and does not stop the program, and any warning can be switched off. An error flags a fatal problem: MATLAB prints the message and stops the current program. The mechanism behind an error is the exception: when MATLAB detects a severe fault in a command or program, it collects information about what was happening, shows a message, and ends the command or program. This is called throwing an exception. The information travels in an object of the class MException, which you can catch with try/catch, examine and throw again.

Tool What it does Stops the program?

error

Throws an exception with a message and an optional identifier.

Yes, unless a try/catch catches it.

MException

The object that carries the identifier, message, stack and causes; also a way to build an exception before throwing it.

Only when thrown.

try/catch

Runs a block and, if it throws, runs the catch block instead of ending the program.

No, it is the way to continue.

warning

Prints a message and records it for lastwarn; states switch it on and off by identifier.

No.

assert

Throws an error if a condition is false.

Yes, like error.

onCleanup

Runs a function when the object is destroyed, including when a function ends by an error.

Not applicable.

Throwing errors with error

error has five syntaxes in the documentation:

error(msg)
error(msg,A1,...,An)
error(errID,___)
error(errorStruct)
error(correction,___)

A message alone. error(msg) throws an error and displays msg. Used with a single argument, the text is shown as written, with no conversion: the documentation notes that you must give more than one argument if you want \n, %s and %d converted, so error('Error. \nInput must be a char.') prints a literal backslash and n.

A message with run-time values. error(msg,A1,...,An) replaces the conversion characters of msg with the values, using the same conversion characters as sprintf. The documentation’s example makes a message that says what was wrong instead of only that something was:

function com = combinations(n,k)
    if k > n
        error('Cannot choose %i from %i elements',k,n)
    end
    com = factorial(n)/(factorial(k)*factorial(n-k));
end

Not run here (the example of the documentation page, which shows combinations(6,9) ending with Error using combinations (line 3) and the message Cannot choose 9 from 6 elements). A percent character in msg is a format specifier; write %% for a literal one.

A message with an identifier. error(errID,___) attaches an identifier to the exception. The identifier lets the program, and the people who call it, tell one error from another and decide what to do about each, which is the basis of the switch on ME.identifier shown below. The identifier is a text made of one or more component fields and a mnemonic field separated by colons, as in component:mnemonic. Each field must start with a letter, the rest may be letters, digits and underscores, and no white space is allowed anywhere. The documentation’s examples are MATLAB:singularMatrix and MATLAB:narginchk:notEnoughInputs.

A structure. error(errorStruct) throws an error from a scalar structure with the fields message, identifier and stack (at least one of them). Unlike the other syntaxes it displays the message as it is, without converting special characters. The stack field is a structure array with the fields file, name and line, the same format as dbstack("-completenames").

A suggested fix. error(correction,___) takes a matlab.lang.correction object (AppendArgumentsCorrection, ConvertToFunctionNotationCorrection or ReplaceIdentifierCorrection) and adds a Did you mean: line to the display. The documentation’s example calls hello without its argument and suggests hello("world"). This is a feature of the MATLAB desktop messages and is shown here only for completeness.

Two more rules from the same page: if all inputs to error are empty, MATLAB does not throw an error; and an error inside a try block does not end the program, it passes control to the catch block.

The next script throws an error with an identifier and a formatted message and reads the pieces back. The catch err form is the same in MATLAB and Octave, but in MATLAB err is an MException and in Octave it is a structure with the same field names (class(err) prints struct in Octave).

try
  error("heatflow:badSize", "Cannot choose %d from %d elements", 9, 6);
catch err
  disp(err.identifier)
  disp(err.message)
  disp(class(err))
end
try
  error("Error. \\nInput must be a char.");
catch err
  disp(err.message)
end
try
  error("heatflow:onlyId");
catch err
  fprintf("id=[%s] msg=[%s]\n", err.identifier, err.message);
end

Octave 11.3.0 output (in MATLAB the first class name would be MException):

heatflow:badSize
Cannot choose 9 from 6 elements
struct
Error. \nInput must be a char.
id=[] msg=[call to error with message identifier 'heatflow:onlyId' requires message]

The last case is a portability trap. Following the documentation, MATLAB’s error(msg) with a single argument treats that text as the message, even when it looks like an identifier; Octave refuses to take an identifier without a message. Always pass the message with an identifier.

The MException object

Any MATLAB code that detects an error constructs an MException object, and the object holds what can be retrieved about the error. Two syntaxes build one yourself:

ME = MException(errID,msg)
ME = MException(errID,msg,A1,...,An)

Both arguments are required: unlike error, an MException always has an identifier. The second form formats msg with the values, as error does. The class has these read-only properties:

Property Type Content

identifier

character vector

The identifier, for example 'myComponent:inputError'.

message

character vector

The message, after the replacement of the conversion characters.

stack

structure array

One element per stack frame with the fields file, name and line; the first element is where the exception was thrown. It is a 0-by-1 structure for an exception that has not been thrown.

cause

cell array of MException

The related exceptions added with addCause; {} if there are none.

Correction

correction object

The suggested fix added with addCorrection, if any.

The documentation lists these object functions:

Function Purpose

throw(ME)

Throws the exception. The stack trace is created from the location where MATLAB calls throw.

rethrow(ME)

Throws a caught exception again and preserves the original stack, so the error still points to where it first occurred.

throwAsCaller(ME)

Throws the exception as if it occurred in the calling function, so the message points to the call, not to the helper that found the fault.

addCause(baseME,causeME)

Appends causeME to the cause property of baseME and returns the modified object.

addCorrection(ME,correction)

Attaches a suggested fix.

getReport(ME)

Returns the error message text for the exception.

MException.last

Static function returning the last uncaught exception; MException.last("reset") clears it. It is not set when a try/catch caught the exception.

throw, rethrow and throwAsCaller

The three differ in the stack trace they leave, which is what you look at when debugging. The documentation compares them with a function combineArrays that calls a helper catAlongDim1, which concatenates two arrays of different sizes. With throw(exception) in the catch block, the displayed error points to the line of the throw, so the information about the helper is lost. Replacing it by rethrow(exception) keeps the original stack: the display shows the failing cat call, then combineArrays>catAlongDim1, then combineArrays. Use rethrow when your catch block cannot fix the problem and the original location is what the next reader needs. throwAsCaller goes the opposite way and hides the helper: in the documentation’s sayHello example, a check function written as a local function makes the message start with Error using sayHello>checkInput when it uses throw, and with Error using sayHello when it uses throwAsCaller. The reference page for throwAsCaller adds a note that throw is recommended over throwAsCaller since R2022b, because it creates the stack trace from the location where MATLAB calls the function.

Not run here (these three behaviours need MException and the display of the MATLAB desktop). In Octave, which has only rethrow(err) on a structure, the original stack is kept as well. This was run:

1;
function check_plate(n)
  if n < 1
    error("heatflow:badPlate", "n must be positive, got %d", n);
  end
end
function outer(n)
  try
    check_plate(n)
  catch e
    rethrow(e)
  end
end
try
  outer(0)
catch e2
  disp(class(e2))
  disp(e2.identifier)
  disp(e2.message)
  disp(fieldnames(e2.stack)')
  for k = 1:numel(e2.stack)
    fprintf("%s line %d\n", e2.stack(k).name, e2.stack(k).line);
  end
end
disp(exist("MException"))

Octave 11.3.0 output, with the code saved as the script rethrowDemo.m (the first line 1; makes the file a script so that it can define functions; MATLAB accepts functions between script statements since R2024a, and earlier releases need them at the end of the file):

struct
heatflow:badPlate
n must be positive, got 0
{
  [1,1] = file
  [1,2] = name
  [1,3] = line
  [1,4] = column
}
check_plate line 4
outer line 9
rethrowDemo line 15
0

The first stack frame is check_plate, where the error was raised, not outer, where rethrow ran. Octave’s stack structure has a fourth field, column; MATLAB’s has file, name and line. The final 0 shows that MException does not exist in Octave.

Adding causes

addCause is for an error that has several reasons. The documentation builds a base exception that says Unable to index into array. and appends one cause per check that failed; when thrown, the display lists them under Caused by:, and baseException.cause{1} and baseException.cause{2} are MException objects of their own. The same idea appears on the page of try: the catch block adds the sizes of the two matrices to the dimension-mismatch error before rethrowing it.

A = rand(3);
B = ones(5);
try
   C = [A; B];
catch ME
   if (strcmp(ME.identifier,'MATLAB:catenate:dimensionMismatch'))
      msg = ['Dimension mismatch occurred: First argument has ', ...
            num2str(size(A,2)),' columns while second has ', ...
            num2str(size(B,2)),' columns.'];
        causeException = MException('MATLAB:myCode:dimensions',msg);
        ME = addCause(ME,causeException);
   end
   rethrow(ME)
end

Not run here (it uses MException and addCause, and the documented display is Error using vertcat, Dimensions of matrices being concatenated are not consistent., then Caused by: and the added line).

Catching errors with try and catch

try
   statements
catch exception
   statements
end

If a statement in the try block throws, MATLAB skips the rest of the block and runs the catch block; if nothing throws, the catch block is skipped. Naming a variable after catch stores the MException in it; leave it out if you need nothing from the exception. Blocks can be nested, and the documentation recommends writing try, catch and end on separate lines; if you put them on one line, separate them with commas, as in try, surf, catch ME, ME.stack, end. Two limits from the reference page: there is no finally block in MATLAB (the tool for guaranteed cleanup is onCleanup, below), and you cannot use several catch blocks in one try; to handle different errors differently, switch on the identifier. You also cannot define nested functions inside a try or catch block.

The documentation names three uses for a catch block: avoid the error and finish in another way, clean up the unwanted side effects of the error, and cope with many problematic inputs or commands. Its first example is a function that computes the number of combinations of k out of n and falls back to the opposite order when k > n, instead of throwing. Treat that as an illustration of the syntax: a catch without a condition also swallows errors you did not expect, which is why the following pattern is better.

Matching on ME.identifier

Decide what to do from the identifier, handle the cases you know, and rethrow all the others. The documentation page for try, catch shows this with switch:

try
    a = notaFunction(5,6);
catch ME
    switch ME.identifier
        case 'MATLAB:UndefinedFunction'
            warning('Function is undefined.  Assigning a value of NaN.');
            a = NaN;
        case 'MATLAB:scriptNotAFunction'
            warning(['Attempting to execute script as function. '...
                'Running script and assigning output a value of 0.']);
            notaFunction;
            a = 0;
        otherwise
            rethrow(ME)
    end
end

Not run here (it needs MATLAB’s own identifiers). The documented output for an undefined function is Warning: Function is undefined. Assigning a value of NaN.

The identifiers of the errors raised by MATLAB itself are the ones you will need most often. The ones below appear in the R2026b reference pages cited on this page; the message texts vary between releases, so match on the identifier and not on the wording.

Identifier Where the documentation shows it

MATLAB:UndefinedFunction

Calling a function that does not exist (try, catch and MException pages).

MATLAB:scriptNotAFunction

Calling a script with arguments or as a function (try, catch page).

MATLAB:badsubscript

Indexing past the end of an array (MException.last page).

MATLAB:catenate:dimensionMismatch

Concatenating arrays whose sizes are not compatible (try, catch page).

MATLAB:narginchk:notEnoughInputs

Calling a function without enough input arguments, for example surf with no inputs (MException and rethrow pages).

A script that matches on identifiers has to be written for the identifiers of the language it runs in. The following one was run in Octave. It raises its own heatflow:divByZero error, handles it, and rethrows anything else; the last three lines show that Octave’s identifiers for the same mistakes are different from MATLAB’s.

1;
function r = safe_div(a, b)
  try
    if b == 0, error("heatflow:divByZero", "division by zero"); end
    r = a / b;
  catch ME
    switch ME.identifier
      case "heatflow:divByZero"
        r = NaN;
      otherwise
        rethrow(ME)
    end
  end
end
disp(safe_div(1, 0))
disp(safe_div(6, 3))
try
  safe_div([1 2 3], [1 2])
catch ME
  fprintf("[%s]\n", ME.identifier);
end
try
  undefined_thing(1)
catch ME
  fprintf("[%s]\n", ME.identifier);
  disp(ME.message)
end
try
  x = [1 2 3]; x(7)
catch ME
  fprintf("[%s]\n", ME.identifier);
  disp(ME.message)
end

Octave 11.3.0 output (MATLAB would print MATLAB:UndefinedFunction and MATLAB:badsubscript for the last two, per the documentation pages above):

NaN
2
[Octave:nonconformant-args]
[Octave:undefined-function]
'undefined_thing' undefined near line 23, column 3
[Octave:index-out-of-bounds]
x(7): out of bound 3 (dimensions are 1x3)

The shape is what matters: known cases handled, everything else rethrown with its original stack. A catch that handles everything and prints nothing hides faults, and the data that was wrong reaches the next step of the program.

What happens to the program after a catch

When the catch block ends, execution continues after the end of the try/catch. If you want the failure to continue upward, end the block with rethrow. The documentation describes the chain: rethrow exits the current function keeping the call stack as it was, returns to the calling function, and so on, until an enclosing try catches it or the program ends at the top level. The next figure shows that chain together with a cleanup object.

sequenceDiagram participant Top as Top-level try/catch participant Mid as Function stepAll participant Low as Function stepPlate participant Clean as onCleanup object Top->>Mid: call Mid->>Low: call Low->>Clean: c = onCleanup(@closeLog) Low-->>Low: error heatflow:unstableStep is thrown Note over Low,Clean: the function ends by the error and its local variables are cleared Low->>Clean: object destroyed, closeLog runs Low-->>Mid: exception propagates up Mid-->>Mid: no try/catch here, function ends Mid-->>Top: exception propagates up Top-->>Top: catch ME, switch on ME.identifier, rethrow if unknown

Figure: an error in stepPlate ends that function, destroys its onCleanup object (so the cleanup function runs), leaves stepAll (which has no try) and is caught at the top level, where the code decides from ME.identifier whether it knows the error.

Warnings

warning is the tool for conditions that do not justify stopping:

warning(msg)
warning(msg,A1,...,An)
warning(warnID,___)
warning
warning(state)
warning(state,warnID)
warnStruct = warning
warning(warnStruct)
modeStruct = warning(state,mode)
warning(modeStruct)

The first three display a warning. The message follows the same rules as for error, including the format specifiers and the need for more than one argument before special characters are converted. An identifier has the same component:mnemonic form as for errors and is what lets users switch one warning off without silencing all the others. A call with an empty msg (warning("")) displays nothing and resets what lastwarn returns.

The others manage states. state is "on", "off" or "query"; warnID is an identifier, "all" or "last" (the last warning issued).

Call Effect

warning("off"), warning("on")

Disable or enable all warnings.

warning("off",warnID)

Disable one warning. warning("off","last") disables the most recent one.

warning("query",warnID)

Return a structure with fields identifier and state.

warning

Display the states of all warnings.

s = warning

Return a structure array of the current states (the first element has the identifier "all"), to restore later.

old = warning("off",warnID)

Disable the warning and return its previous state.

warning(s)

Restore the states saved in s.

warning("on","verbose"), warning("off","backtrace")

Control the display: "verbose" (default off) adds a hint about how to suppress the warning; "backtrace" (default on) shows the stack trace.

"All warnings" means only the warnings issued or modified during the current session; modified states last until the end of the session, so a state you leave changed affects the rest of the session. For that reason, switch a warning off around the one call that causes it and restore the previous state, which is the pattern in the documentation:

A = eye(2);
B = [3 6; 4 8];
C = B\A;
[msgStr,warnId] = lastwarn;
warnStruct = warning("off",warnId);
C = B\A;
warning(warnStruct);

Not run here: this is the documented sequence, where the singular matrix warning has the identifier MATLAB:singularMatrix and message Matrix is singular to working precision.. A second documented way to find the identifier of a warning is w = warning("query","last"); warning("query","last") returns only the last warning displayed, because MATLAB displays only warnings whose state is "on" and that have an identifier.

lastwarn returns the last warning message and, as a second output, its identifier, regardless of whether the warning was displayed: [msg,warnID] = lastwarn. lastwarn(newMsg,newID) sets them, and the identifier is an empty character vector if the warning had none.

The next script runs the same sequence with a heatflow warning identifier.

warning("heatflow:coarseGrid", "Grid %dx%d is coarse", 3, 3);
[msg, id] = lastwarn();
fprintf("msg=[%s] id=[%s]\n", msg, id);
warning("off", "heatflow:coarseGrid");
warning("heatflow:coarseGrid", "Grid %dx%d is coarse", 4, 4);
[msg, id] = lastwarn();
fprintf("after off: msg=[%s]\n", msg);
s = warning("query", "heatflow:coarseGrid");
disp(s.state)
warning("on", "heatflow:coarseGrid");
old = warning("off", "heatflow:coarseGrid");
disp(class(old))
warning("heatflow:coarseGrid", "silent");
warning(old);
warning("heatflow:coarseGrid", "visible again");
lastwarn("");
[msg, id] = lastwarn();
fprintf("reset: [%s] [%s]\n", msg, id);

Octave 11.3.0 output, with the code saved as the script coarseGridWarning.m (Octave prints the called from lines, MATLAB prints Warning: followed by the message and, with backtrace on, an In line):

warning: Grid 3x3 is coarse
warning: called from
    coarseGridWarning at line 1 column 1

msg=[Grid 3x3 is coarse] id=[heatflow:coarseGrid]
after off: msg=[Grid 4x4 is coarse]
off
struct
warning: visible again
warning: called from
    coarseGridWarning at line 16 column 1

reset: [] []

The warning issued while the identifier was off is not displayed, but lastwarn still has it (after off), exactly as the documentation says. The warning issued between old = warning("off", ...) and warning(old) is silent too, and after the restore the warning shows again.

Checking assumptions with assert

assert(cond) throws an error if cond is false. The messages can be added in the same way as for error:

assert(cond)
assert(cond,msg)
assert(cond,msg,A)
assert(cond,errID,msg)
assert(cond,errID,msg,A)

cond is an expression that is logical or can be converted to a logical, and it may combine relational and logical operators. Use assert for conditions that should never happen in correct code (the documentation’s phrase is conditions that "should not happen in normal code execution"), for example that the roots a function computed really satisfy the equation. For checking the arguments a caller passes, use argument validation (see Argument Validation), which produces clearer messages from declarations. Like any error, a failed assertion inside a try block does not end the program; control goes to the catch block.

x = 26;
assert(7 < x)
try
  assert(x < 13, "heatflow:range", "x=%d out of range", x);
catch ME
  fprintf("[%s] %s\n", ME.identifier, ME.message);
end
try
  assert(isa(single(1)*13, "double"), "Product is type %s, not double.", class(single(1)*13));
catch ME
  fprintf("[%s] %s\n", ME.identifier, ME.message);
end
try
  assert(false)
catch ME
  disp(ME.message)
end

Octave 11.3.0 output (in MATLAB a failed assert(cond) has the message Assertion failed. and the error identifier MATLAB:assertion:failed, not run here; Octave prints its own message):

[heatflow:range] x=26 out of range
[] Product is type single, not double.
assert (false) failed

Octave’s assert is also a testing function that compares a value with an expected one (assert(observed, expected, tol)), which MATLAB’s does not. Code that uses that form does not run in MATLAB.

Cleaning up with onCleanup

Because there is no finally, the way to guarantee that a file is closed, a folder restored or a figure closed is an onCleanup object:

cleanupObj = onCleanup(cleanupFun)
cancel(cleanupObj)

onCleanup creates an object that runs the function handle cleanupFun when the object is destroyed. MATLAB clears all local variables when a function ends, whether by normal completion or by a forced exit, such as an error or Ctrl+C, so a cleanup object held in a local variable runs at every exit. The documentation’s advice for using it:

  • Pass an anonymous function handle so that you can give arguments to the cleanup task, as in @() fclose(fid).

  • If the tasks depend on the order of execution, define one object that calls a function or script with all the tasks: MATLAB does not guarantee the order in which it destroys several objects.

  • Avoid nested functions as cleanup tasks, because MATLAB can clear the variables they use before they run.

  • If you reference or pass the object outside the function, the cleanup does not run when the function ends but when the object is destroyed.

  • Saving an onCleanup object gives a warning and does not save the task.

cancel (R2025a). cancel(cleanupObj), also written cleanupObj.cancel, cancels the tasks and prevents them from running when the object is destroyed. The documentation’s example prints Function was canceled prematurely from the cleanup function when the function is interrupted with Ctrl+C, and cancels the task at the end of a normal run so that the message is not shown. Use it for cleanup that is only needed on failure, such as deleting a half-written output file.

Not run here (cancel does not exist in Octave). The code below runs in both languages and shows what a cleanup object does when a function ends normally and when it ends by an error.

1;
function work(doFail)
  disp("open")
  c = onCleanup(@() disp("cleanup ran"));
  if doFail, error("heatflow:boom", "failed"); end
  disp("end of work")
end
work(false)
try
  work(true)
catch ME
  disp(ME.message)
end
disp("after")

Octave 11.3.0 output:

open
end of work
cleanup ran
open
cleanup ran
failed
after

Note the order in the second call: the cleanup ran before the catch block printed the message. Octave also has unwind_protect …​ unwind_protect_cleanup …​ end_unwind_protect, which is the nearest thing to finally there but is not MATLAB syntax; do not use it in code meant for both.

Reporting errors in an application

The documentation’s guidance for programs, as opposed to the Command Window, comes down to three roles.

  1. Detect and throw. Where the code finds a condition that makes the program fail or give unacceptable results, it throws an exception that carries what went wrong and where. Capture it in an MException with an identifier, a message with the values, and optionally causes.

  2. Catch where you can do something. A catch block can examine the error, gather more information for the user, try another way, or clean up the side effects of the error; at the end it can continue or finish. If it cannot do any of that, it should not catch.

  3. Report at the top. Whatever reaches the top of the application is turned into something the user can act on: the message, and, for a developer, the stack. At the Command Window the documentation’s own advice for finding a fault is to read the message, follow the line number to the failing line in the Editor, and use the Debugger, with dbstop if error as a help to find where the error happens (see Debugging).

Practical consequences, which follow from the reference pages above:

  • Give every error you throw an identifier, built as component:mnemonic or component:subcomponent:mnemonic, with your own component name (the heatflow namespace of the scenario) so that it can never be mistaken for a MATLAB: identifier.

  • Put the values in the message (%d, %g, %s), not only the fact.

  • Match on the identifier, never on the message text.

  • Rethrow what you do not handle, with rethrow, so that the stack still points to the origin.

  • Use throwAsCaller for a check function whose own location is of no interest to the user, and throw otherwise.

  • Use warning for conditions the program survives, with an identifier so that callers can switch it off.

  • Use onCleanup for resources.

A heatflow time step has all of that in a few lines. It raises an error when the step is too large for the explicit scheme (the stability limit of the two-dimensional explicit method is a ratio of 0.25), a warning when it is close to the limit, and a top-level loop skips the unstable steps but lets every other error through. The code uses only error, warning and try/catch, so it ran in Octave.

runAll([0.1 0.19 0.5])

function T = step_plate(T, alpha, dt, dx)
  r = alpha * dt / dx^2;
  if r > 0.25
    error("heatflow:unstableStep", ...
          "alpha*dt/dx^2 = %.3f exceeds 0.25; reduce dt", r);
  elseif r > 0.2
    warning("heatflow:nearStabilityLimit", ...
            "alpha*dt/dx^2 = %.3f is close to 0.25", r);
  end
  T(2:end-1, 2:end-1) = T(2:end-1, 2:end-1) + r * ( ...
      T(1:end-2, 2:end-1) + T(3:end, 2:end-1) + ...
      T(2:end-1, 1:end-2) + T(2:end-1, 3:end) - 4 * T(2:end-1, 2:end-1));
end
function runAll(dts)
  T = zeros(5); T(3,3) = 100;
  for dt = dts
    try
      T2 = step_plate(T, 1.1e-4, dt, 0.01);
      fprintf("dt=%g ok, max=%.2f\n", dt, max(T2(:)));
    catch err
      switch err.identifier
        case "heatflow:unstableStep"
          fprintf("dt=%g skipped: %s\n", dt, err.message);
        otherwise
          rethrow(err)
      end
    end
  end
end

The file is a script: its first statement is the call, and the two local functions follow at the end of the file, a layout MATLAB accepts in every release that has local functions in scripts. Octave needs a function to be defined before a script calls it, so the run used the same code with the call moved after the functions and a 1; line at the top. Octave 11.3.0 output, with Octave’s called from lines for the warning trimmed:

dt=0.1 ok, max=56.00
warning: alpha*dt/dx^2 = 0.209 is close to 0.25
dt=0.19 ok, max=20.90
dt=0.5 skipped: alpha*dt/dx^2 = 0.550 exceeds 0.25; reduce dt

A file must not begin with a function line if it is meant to be a script, because then MATLAB treats it as a function file (see Scripts and Code Sections). Argument checking of alpha, dt and dx belongs in an arguments block, as in Argument Validation.

Pitfalls

Pitfall What to do

A bare catch that swallows every error.

Name the exception, switch on the identifier, and rethrow the unknown ones.

Matching on the text of ME.message.

Match on ME.identifier; messages change between releases and between languages (MATLAB, Octave).

Calling error("component:mnemonic") with only an identifier.

With one argument MATLAB takes the text as the message (the documentation’s error(msg)); Octave rejects it. Always pass a message too.

error("Line one\nLine two") with \n.

With a single argument the escape is not converted. Pass an argument too, or build the text with newline or sprintf.

throw(ME) in a catch block, losing the origin.

Use rethrow(ME) to keep the original stack.

Waiting for a finally block.

There is none; use onCleanup, held in a local variable.

Several onCleanup objects whose order matters.

MATLAB does not guarantee the order; use one object that calls one function.

A warning("off","all") left in the code.

The state lasts for the session. Switch off one identifier and restore the saved state with warning(old).

Looking for the identifier of a warning that was not displayed.

Use lastwarn, which returns the last warning whether it was displayed or not; warning("query","last") returns only displayed ones.

Reading MException.last inside a catch block.

MException.last is the last uncaught exception, so it is not set when a try/catch caught the error. In a catch block, use the variable.

Using assert on arguments supplied by a caller.

Use argument validation; keep assert for conditions that should never happen.

Copying Octave’s assert(observed,expected) or unwind_protect into MATLAB.

They are not MATLAB syntax.

Where older books differ

Older books and older code may handle errors with lasterr, lasterror or the implicit catch of eval, and treat the identifier as an optional extra. The current documentation builds everything on the MException object: the catch ME variable, matching on ME.identifier, rethrow(ME) and addCause, and recommends the structured form over parsing messages. onCleanup (R2008a) replaces the pattern of cleaning up in every branch of a function, and its cancel method (R2025a) is newer than any book. The documentation also marks throwAsCaller as less preferable than throw since R2022b. Where code catches errors with eval("code","catch code"), replace it with try/catch (see eval and Its Alternatives).