Handle Errors and Timeouts
By the end of this lesson, you'll be able to:
- Handle a non-200 response with a clear, specific exception
- Handle a CalloutException from a genuine network failure or timeout
- Explain why these are two genuinely different failure modes
Prerequisites: "Parse the Response (JSON)"
Handling a non-200 response
public class CurrencyConversionException extends Exception {}
public Decimal convert(Decimal amountInZar, String targetCurrency) {
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Currency_API/convert?from=ZAR&to=' + targetCurrency + '&amount=' + amountInZar);
request.setMethod('GET');
HttpResponse response = new Http().send(request);
if (response.getStatusCode() != 200) {
throw new CurrencyConversionException(
'Currency API returned status ' + response.getStatusCode() + ': ' + response.getBody()
);
}
ConversionResponse parsed = (ConversionResponse) JSON.deserialize(response.getBody(), ConversionResponse.class);
return parsed.convertedAmount;
}
public class CurrencyConversionException extends Exception {}— Module 22's custom exception pattern, once more.- (blank line)
public Decimal convert(Decimal amountInZar, String targetCurrency) {— the method signature.HttpRequest request = new HttpRequest();— an empty request.request.setEndpoint('callout:Currency_API/convert?from=ZAR&to=' + targetCurrency + '&amount=' + amountInZar);— unchanged.request.setMethod('GET');— unchanged.- (blank line)
HttpResponse response = new Http().send(request);— sends it.- (blank line)
if (response.getStatusCode() != 200) {— checked before trusting the body at all.throw new CurrencyConversionException(— opens the exception.'Currency API returned status ' + response.getStatusCode() + ': ' + response.getBody()— carries the actual status and body, so whoever catches it knows exactly what went wrong.);— closes the constructor call.}— closes theif.- (blank line)
ConversionResponse parsed = (ConversionResponse) JSON.deserialize(response.getBody(), ConversionResponse.class);— only reached once a200is confirmed.return parsed.convertedAmount;— the final result.}— closes the method.
Module 22's custom exception pattern, once more — CurrencyConversionException carries the actual status code and response body, so whoever catches it (or reads the debug log, Module 33) knows exactly what the external API said went wrong, e.g. an unrecognized currency code (Lesson 1's edge case).
Handling a network failure or timeout
public Decimal convert(Decimal amountInZar, String targetCurrency) {
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Currency_API/convert?from=ZAR&to=' + targetCurrency + '&amount=' + amountInZar);
request.setMethod('GET');
request.setTimeout(5000); // 5 seconds, well under the 10-second callout limit
HttpResponse response;
try {
response = new Http().send(request);
} catch (CalloutException e) {
throw new CurrencyConversionException('Could not reach the currency service: ' + e.getMessage());
}
if (response.getStatusCode() != 200) {
throw new CurrencyConversionException('Currency API returned status ' + response.getStatusCode());
}
ConversionResponse parsed = (ConversionResponse) JSON.deserialize(response.getBody(), ConversionResponse.class);
return parsed.convertedAmount;
}
public Decimal convert(Decimal amountInZar, String targetCurrency) {— the method signature.HttpRequest request = new HttpRequest();— an empty request.request.setEndpoint('callout:Currency_API/convert?from=ZAR&to=' + targetCurrency + '&amount=' + amountInZar);— unchanged.request.setMethod('GET');— unchanged.request.setTimeout(5000); // 5 seconds, well under the 10-second callout limit— a new, explicit timeout.- (blank line)
HttpResponse response;— declared outside thetryso it's usable afterward.try {— Module 20'stry/catch, applied to a callout.response = new Http().send(request);— the callout itself, which might fail to complete at all.} catch (CalloutException e) {— catches a genuine network failure or timeout — no response was ever received.throw new CurrencyConversionException('Could not reach the currency service: ' + e.getMessage());— re-wraps it in the service's own exception type.}— closes thetry/catch.- (blank line)
if (response.getStatusCode() != 200) {— a different failure mode: a response was received, just an error one.throw new CurrencyConversionException('Currency API returned status ' + response.getStatusCode());— Lesson 5's earlier error handling, unchanged.}— closes theif.- (blank line)
ConversionResponse parsed = (ConversionResponse) JSON.deserialize(response.getBody(), ConversionResponse.class);— only reached once a real200response exists.return parsed.convertedAmount;— the final result.}— closes the method.
A CalloutException (Module 20's try/catch, applied here) means the request never even got a response at all — a genuine network failure, a DNS problem, or exceeding setTimeout's limit — a fundamentally different failure from "the API responded, but with an error status." Both get wrapped in the same CurrencyConversionException for a consistent interface to callers, but they're detected and handled at different points in the method.
Exercise
As a comment, explain the difference between a non-200 HttpResponse and a caught CalloutException.
Show hint
Think about whether a response was actually received in each case.
Handle Errors and Timeouts Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
A real integration can fail in two distinct ways — the external API responds with an error, or the callout itself fails to complete at all — and this lesson handles both explicitly.