Skip to content

Error Handling

Returning Error Responses

The most common pattern: check for errors in your handler and return an appropriate HTTP status code.

cpp
using namespace framework;
using namespace framework::clients::http;

app.register_endpoint(
    http_verb_t::get,
    "/users/(\\d+)",
    [](const http_request& req) -> http_response_t {
        auto& params = req.http_params_.path_;
        auto it = params.find("1");

        if (it == params.end()) {
            auto res = helpers::make_base_http_response(req, http_status_t::bad_request);
            auto body = helpers::make_base_http_payload(400, "Missing ID");
            helpers::finalize_response(res, body);
            return res;
        }

        auto res = helpers::make_base_http_response(req, http_status_t::ok);
        auto body = helpers::make_base_http_payload(200, "ok");
        body["user"] = {{"id", it->second}};
        helpers::finalize_response(res, body);
        return res;
    }
);

HTTP Status Codes Reference

CodeNameWhen to Use
200OKSuccessful response
201CreatedResource was created
204No ContentSuccess, no body needed
400Bad RequestMalformed input
401UnauthorizedMissing or invalid JWT
403ForbiddenValid JWT but insufficient permissions
404Not FoundResource does not exist
422Unprocessable EntityValidation failure
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error

Usage:

cpp
res.result(http_status_t::not_found);
res.result(http_status_t::internal_server_error);

Validation Errors

When a validator attached to a route returns {false, errors}, the framework automatically returns a 422 response. The error response body contains the field-level error map.

json
{
  "name": ["The name field is required."],
  "email": ["The email field is required."]
}

See Validation for details on writing validators.


Startup Errors

If the framework fails to start (e.g., port already in use, invalid configuration), app.run() may throw std::exception. Wrap the call in try-catch to handle startup failures. Note that app.run() is blocking — the try-catch only covers startup errors, not runtime errors that occur after the service is running.

cpp
int main() {
    try {
        framework::app app;
        // ... configure ...
        app.run();  // blocks until SIGINT/SIGTERM
    } catch (const std::exception& e) {
        fmt::println(stderr, "Startup failed: {}", e.what());
        return 1;
    }
    return 0;
}