Currently Available: Need a skilled Software Developer for your next project?
Categories
Laravel

Handling Failures in the Laravel HTTP Client

When PHP code sends an HTTP request with Laravel’s HTTP client, failure takes one of two forms: the server returns an error status, or a connection problem prevents the client from getting a response. Laravel handles these cases differently. By default, a 4xx or 5xx response is returned for your code to inspect; a connection failure can throw an exception.

Handle each failure based on what happened. Check the response when a status such as 404 is an expected outcome. Call throw() when an error status should stop the normal code path. Catch connection exceptions separately when the request fails before the server sends a response.

Laravel returns error responses by default

An HTTP response has a status code, such as 404. For 4xx and 5xx status codes, Laravel’s HTTP client returns a response object instead of automatically throwing an exception. A timeout or DNS failure means the request did not produce a response to inspect. Laravel can throw a ConnectionException for these connection failures. The Laravel HTTP client documentation describes these as separate failure paths.

You can check a returned response with methods such as successful(), failed(), clientError(), and serverError(). Laravel classifies 2xx responses as successful, and failed() checks for 4xx or 5xx responses. It does not classify a 3xx redirect as a failure, so handle redirects separately if they matter to your request.

$response = Http::get($url);

if ($response->notFound()) {
    return null;
}

if ($response->failed()) {
    // Handle other 4xx or 5xx responses.
}

return $response->json();

Checking the response lets your code handle expected statuses as normal branches. For example, a lookup can treat 404 as “no record” and handle other 4xx or 5xx responses differently.

Use throw() when an error status should interrupt the request flow

Call throw() on a response when a 4xx or 5xx status should stop the code’s normal path. Laravel then throws an Illuminate\Http\Client\RequestException, an exception that carries the error response. If the status is not an error, throw() returns the response and the code can keep using it.

$response = Http::post($url, $payload)->throw();

return $response->json();

Check the response when its status determines what the code should do. A 404 might mean a record is absent, while a 422 response might contain validation details for the caller to handle. When a status should stop normal processing, throw() raises an exception that a surrounding catch block can handle.

Laravel also provides conditional throw methods, including throwIfStatus(403) and throwIfClientError(). The HTTP client documentation lists these alongside the response-check methods.

Catch status and connection failures separately

Catch RequestException when throw() turns an error response into an exception. Its response property gives you access to the status code and response body. Catch ConnectionException for failures such as timeouts, where there may be no response to inspect.

use Illuminate\Http\Client\ConnectionException;
use Illuminate\Http\Client\RequestException;

try {
    $response = Http::post($url, $payload)->throw();

    return $response->json();
} catch (RequestException $e) {
    $status = $e->response->status();

    // Inspect the response or translate the error for the caller.
} catch (ConnectionException $e) {
    // Handle a request that failed before returning a response.
}

Choose how to handle the error based on what the caller can do next. For a RequestException, inspect the server’s response and decide whether to report a validation error, handle a conflict, or log an upstream failure. For a ConnectionException, handle the fact that the request received no response. A broad catch can make sense at a boundary that handles all unexpected errors, but it should not hide the difference when these failures need different responses.

Choose the control flow that matches the status

Use response checks when a status is an expected branch in the integration. Use throw() when an error response makes the rest of the operation invalid and an exception-handling boundary will deal with it. In either case, account for connection failures separately if the request must handle them.

Laravel also supports retries, but you should not automatically retry every failure. For example, a request that changes data needs a retry policy suited to that action. Laravel’s retry documentation shows how a callback can decide whether to retry based on the exception. It also notes that setting throw: false returns the last failed response after retries, while connection failures still throw a ConnectionException.

What I'm building

Delegate tasks. Get software.

Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.

Take a look at vroni.com

Email updates

Usually a new article and a few links I found interesting.

No spam. Unsubscribe with one click.

Leave a Reply

Your email address will not be published. Required fields are marked *