Error Handling and Debugging

error('Id:component', 'message %d', value) stops execution with an identifier and a formatted message.

Concept

error('Id:component', 'message %d', value) stops execution with an identifier and a formatted message. warning(...) prints a message and continues. assert(condition, message) is a compact check.

Branch on err.identifier to recover from expected problems and rethrow the rest. onCleanup(@() ...) guarantees cleanup (closing files, instruments) even when an error occurs.

Breakpoints (click the margin, or dbstop in file at line) dbstop if error Stop where an error happens dbstep, dbcont, dbquit, dbstack

Step, continue, quit, show the call stack Workspace / data tips Inspect variables while paused Pause at a point in code

Code Analyzer (MATLAB editor) / checkcode Static warnings (unused variables, missing semicolons, deprecated functions) lasterr, MException.last The last error

try
    risky();
catch err                       % an MException object
    err.identifier, err.message, err.stack
    rethrow(err)                % or recover
end

Why It Matters

Measurement scripts meet missing files, bad values and disconnected instruments. Robust error handling turns a crash at 2 a.m. into a logged, recoverable event.

Syntax

error('Bench:badChannel', 'Channel %d does not exist', ch);
try ... catch err, fprintf('%s\n', err.message); end
c = onCleanup(@() fclose(fid));

Example

20.1 Throwing, catching by identifier, common runtime errors, assert and onCleanup

The helper function that simulates an instrument:

function value = read_sensor(channel)
%READ_SENSOR Returns a simulated reading, or throws a structured error.
    if ~ismember(channel, 1:4)
        error('Bench:badChannel', 'Channel %d does not exist (valid: 1-4)', channel);
    end
    if channel == 4
        error('Bench:timeout', 'Sensor on channel %d did not answer', channel);
    end
    value = 12 + channel / 10;
end
for ch = [2 4 7]
    try
        v = read_sensor(ch);
        fprintf('channel %d: %.1f V\n', ch, v)
    catch err
        switch err.identifier
            case 'Bench:timeout'
                fprintf('channel %d: timeout -> using last known value (%s)\n', ch, err.message)
            otherwise
                fprintf('channel %d: error %s: %s\n', ch, err.identifier, err.message)
        end
    end
end

try
    x = [1 2 3];
    y = x(5);
catch err
    fprintf('indexing error caught: %s\n', err.message)
end

try
    A = ones(2, 3); B = ones(2, 3);
    C = A * B;
catch err
    fprintf('matrix error caught: %s\n', err.message)
end

warning('off', 'all');
w = str2double('abc');
fprintf('str2double(''abc'') = %g (NaN, not an error) -> check with isnan: %d\n', w, isnan(w))
w = str2double('12,5');
fprintf('str2double(''12,5'') = %g (comma read as a thousands separator, not 12.5!)\n', w)

assert(abs(celsius_to_fahrenheit(100) - 212) < 1e-12, 'conversion is wrong')
disp('assert passed')

cleaner = onCleanup(@() disp('onCleanup ran: resources released'));
clear cleaner

Expected Result

The timeout was handled as a recoverable case while the invalid channel was reported with its identifier. Indexing past the end and a matrix-dimension mismatch were caught with their messages (MATLAB's wording differs slightly, e.g. "Index exceeds the number of array elements"). str2double returns NaN for text that is not a number rather than throwing, so it must be checked explicitly — and a decimal comma is a trap: '12,5' is read with the comma as a thousands separator and gives 125, not 12.5. onCleanup ran its function when the object was cleared.

channel 2: 12.2 V
channel 4: timeout -> using last known value (Sensor on channel 4 did not answer)
channel 7: error Bench:badChannel: Channel 7 does not exist (valid: 1-4)
indexing error caught: x(5): out of bound 3 (dimensions are 1x3)
matrix error caught: operator *: nonconformant arguments (op1 is 2x3, op2 is 2x3)
str2double('abc') = NaN (NaN, not an error) -> check with isnan: 1
str2double('12,5') = 125 (comma read as a thousands separator, not 12.5!)
assert passed
onCleanup ran: resources released

Common Mistakes

Empty catch blocks that hide real errors.

Error messages without identifiers (can't be handled selectively).

Forgetting to close files/instruments when an error occurs.

Debugging by adding disp everywhere instead of using breakpoints.

Best Practices

Use identifiers of the form Component:problem.

Catch only what you can handle; rethrow the rest.

Use dbstop if error while developing.

Validate inputs at function entry.

Interview Relevance

Medium: "how do you handle errors in MATLAB?", "what is an MException?", "how do you debug a script?", "what is onCleanup for?".

Practice Questions

Wrap a fopen in try/catch and report a friendly message if the file is missing.

Write a function that raises an error with an identifier for negative input and test it.

Use dbstop if error on a script that indexes out of bounds and inspect the variables.

Use onCleanup to restore a changed format setting when a function exits.

Quick Quiz

Q1. (Multiple choice) Which object does catch err receive? A) A struct B) An MException C) A string D) A cell Answer: B.

Q2. (Predict the output) For channel 4, what did the example print?

Answer: A timeout message with "using last known value" (verified).

Q3. (Predict the output) str2double('12,5') Answer: 125 — the comma is treated as a thousands separator, not a decimal point (verified). Replace , with . first when parsing European-format numbers: str2double(strrep(s, ',', '.')).

Q4. (Find the bug) try, data = load(file); catch, end — later code fails with "undefined variable data".

Answer: The empty catch hid the load error; handle it (report, default value) or let it propagate.

Coding Challenge

Write a function-free script that tries to convert the texts {'12.6', 'abc', '13.1'} to numbers, raises an error with identifier Parse:bad for invalid text, catches it, and reports which entries were bad while summing the valid ones.

texts = {'12.6', 'abc', '13.1'};
total = 0;
for k = 1:numel(texts)
    try
        v = str2double(texts{k});
        if isnan(v)
            error('Parse:bad', 'entry %d ("%s") is not a number', k, texts{k});
        end
        total = total + v;
    catch err
        fprintf('%s: %s\n', err.identifier, err.message)
    end
end
fprintf('sum of valid entries: %.1f\n', total)