Improve handling of error responses
This page is a capture in the next bucket of the product backlog — a pre-sprint idea, not yet pulled into a sprint as a story.
As per Gemini code review:
Certainly. Point #2 from the review of `CurrencyHistoryDialog.cpp` addressed
the potential complexity of error checking by suggesting that relying on the
specific response message type is **fragile**.
The goal is to move from:
1. Client sends **Request A**.
2. Server returns **Response A** (Success) OR **Error Response** (Failure) OR
**Response B** (Unexpected success type).
3. Client checks: *Is the message type exactly **Response A**?*
to a more robust pattern where the client checks for a generic failure response
first.
-----
## 🐞 Fragile Error Check (Current Code)
The current code in `loadHistory` checks for success by expecting *only* the
specific success message type:
```cpp
// Current Fragile Logic
if (result->header().type != comms::protocol::message_type::get_currency_history_response) {
onHistoryLoadError(QString("Server does not support currency history (received message type %1)")
.arg(static_cast<int>(result->header().type)));
return;
}
```
This logic has two main problems:
1. **Hiding Server Errors:** If the server returns a generic protocol error
(`message_type::error_response`) because, for example, the client's session
timed out, the client logs a misleading message: "Server does not support
currency history." It should be reporting the actual error message sent by
the server.
2. **Lack of Standardization:** Every client method needs to implement its own
logic to handle unexpected types.
-----
## 🛠️ Suggested Improvement: Standardized Error Handling
The improvement is to check for a generic **`error_response`** message type
first, and report its payload/message, before attempting to deserialize the
successful response.
Assuming your system has a standard `error_response` message:
```cpp
void CurrencyHistoryDialog::handleHistoryResponse(const HistoryResult& result) {
if (!result) {
onHistoryLoadError(QString::fromStdString(result.error()));
return;
}
// 1. Check for a generic server-side error response
if (result->header().type == comms::protocol::message_type::error_response) {
// Assume error_response contains a readable message payload
auto error_response = risk::messaging::error_response::deserialize(result->payload());
if (error_response) {
onHistoryLoadError(QString::fromStdString(error_response->message));
} else {
onHistoryLoadError("Server returned a malformed error response.");
}
return;
}
// 2. Check for the specific SUCCESS response type
if (result->header().type == comms::protocol::message_type::get_currency_history_response) {
auto response = risk::messaging::get_currency_history_response::deserialize(result->payload());
if (!response || !response->success) {
// Handle success=false within the expected response type
onHistoryLoadError(QString::fromStdString(response ? response->message : "Invalid or failed history response."));
return;
}
history_ = std::move(response->history);
onHistoryLoaded();
return;
}
// 3. Handle truly unexpected message type
onHistoryLoadError(QString("Received unexpected message type %1 from server.")
.arg(static_cast<int>(result->header().type)));
}
```
By standardizing the **`error_response`** type, the client can always extract
and display the relevant server-side failure reason, leading to much clearer
logging and user feedback.