All C++ client operations return a fluss::Result struct instead of throwing exceptions. This gives you explicit control over error handling.
Result Struct#include "fluss.hpp" // All operations return fluss::Result fluss::Result result = admin.CreateTable(path, descriptor); if (!result.Ok()) { std::cerr << "Error code: " << result.error_code << std::endl; std::cerr << "Error message: " << result.error_message << std::endl; }
| Field / Method | Type | Description |
|---|---|---|
error_code | int32_t | 0 for success, non-zero for errors |
error_message | std::string | Human-readable error description |
Ok() | bool | Returns true if the operation succeeded |
Check the Result after each operation and decide how to respond, e.g. log and continue, retry, or abort:
fluss::Connection conn; fluss::Result result = fluss::Connection::Create(config, conn); if (!result.Ok()) { // Log, retry, or propagate the error as appropriate std::cerr << "Connection failed (code " << result.error_code << "): " << result.error_message << std::endl; return 1; }
Use Available() to verify that a connection or object is valid before using it:
fluss::Connection conn; if (!conn.Available()) { // Connection not initialized or already moved } fluss::Configuration config; config.bootstrap_servers = "127.0.0.1:9123"; fluss::Result result = fluss::Connection::Create(config, conn); if (result.Ok() && conn.Available()) { // Connection is ready to use }
Server-side errors carry a specific error code (>0 or -1). Client-side errors (connection failures, type mismatches, etc.) use ErrorCode::CLIENT_ERROR (-2). Use fluss::ErrorCode to match on specific codes:
fluss::Result result = admin.DropTable(table_path); if (!result.Ok()) { if (result.error_code == fluss::ErrorCode::TABLE_NOT_EXIST) { std::cerr << "Table does not exist" << std::endl; } else if (result.error_code == fluss::ErrorCode::PARTITION_NOT_EXISTS) { std::cerr << "Partition does not exist" << std::endl; } else if (result.error_code == fluss::ErrorCode::CLIENT_ERROR) { std::cerr << "Client-side error: " << result.error_message << std::endl; } else { std::cerr << "Server error (code " << result.error_code << "): " << result.error_message << std::endl; } }
| Constant | Code | Description |
|---|---|---|
ErrorCode::CLIENT_ERROR | -2 | Client-side error (not from server) |
ErrorCode::UNKNOWN_SERVER_ERROR | -1 | Unexpected server error |
ErrorCode::NETWORK_EXCEPTION | 1 | Server disconnected before response |
ErrorCode::DATABASE_NOT_EXIST | 4 | Database does not exist |
ErrorCode::DATABASE_ALREADY_EXIST | 6 | Database already exists |
ErrorCode::TABLE_NOT_EXIST | 7 | Table does not exist |
ErrorCode::TABLE_ALREADY_EXIST | 8 | Table already exists |
ErrorCode::INVALID_TABLE_EXCEPTION | 15 | Invalid table operation |
ErrorCode::REQUEST_TIME_OUT | 25 | Request timed out |
ErrorCode::PARTITION_NOT_EXISTS | 36 | Partition does not exist |
ErrorCode::PARTITION_ALREADY_EXISTS | 42 | Partition already exists |
ErrorCode::PARTITION_SPEC_INVALID_EXCEPTION | 43 | Invalid partition spec |
ErrorCode::LEADER_NOT_AVAILABLE_EXCEPTION | 44 | No leader available for partition |
ErrorCode::AUTHENTICATE_EXCEPTION | 46 | Authentication failed (bad credentials) |
See fluss::ErrorCode in fluss.hpp for the full list of named constants.
Some errors are transient, where the server may be temporarily unavailable, mid-election, or under load. IsRetriable() can be used for deciding to to retry an operation rather than treating the error as permanent.
ErrorCode::IsRetriable(int32_t code) is a static helper available directly on the error code:
fluss::Result result = writer.Append(row); if (!result.Ok()) { if (result.IsRetriable()) { // Transient failure — safe to retry } else { // Permanent failure — log and abort std::cerr << "Fatal error (code " << result.error_code << "): " << result.error_message << std::endl; } }
Result::IsRetriable() delegates to ErrorCode::IsRetriable(), so you can also call it directly on the code:
if (fluss::ErrorCode::IsRetriable(result.error_code)) { // retry }
| Constant | Code | Reason |
|---|---|---|
ErrorCode::NETWORK_EXCEPTION | 1 | Server disconnected |
ErrorCode::CORRUPT_MESSAGE | 3 | CRC or size error |
ErrorCode::SCHEMA_NOT_EXIST | 9 | Schema may not exist |
ErrorCode::LOG_STORAGE_EXCEPTION | 10 | Transient log storage error |
ErrorCode::KV_STORAGE_EXCEPTION | 11 | Transient KV storage error |
ErrorCode::NOT_LEADER_OR_FOLLOWER | 12 | Leader election in progress |
ErrorCode::CORRUPT_RECORD_EXCEPTION | 14 | Corrupt record |
ErrorCode::UNKNOWN_TABLE_OR_BUCKET_EXCEPTION | 21 | Metadata not yet available |
ErrorCode::REQUEST_TIME_OUT | 25 | Request timed out |
ErrorCode::STORAGE_EXCEPTION | 26 | Transient storage error |
ErrorCode::NOT_ENOUGH_REPLICAS_AFTER_APPEND_EXCEPTION | 28 | Wrote to server but with low ISR size |
ErrorCode::NOT_ENOUGH_REPLICAS_EXCEPTION | 29 | Low ISR size at write time |
ErrorCode::LEADER_NOT_AVAILABLE_EXCEPTION | 44 | No leader available for partition |
Client-side errors (ErrorCode::CLIENT_ERROR, code -2) always return false from IsRetriable().
The cluster is not running or the address is incorrect:
fluss::Configuration config; config.bootstrap_servers = "127.0.0.1:9123"; fluss::Connection conn; fluss::Result result = fluss::Connection::Create(config, conn); if (!result.Ok()) { // "Connection refused" or timeout error std::cerr << "Cannot connect to cluster: " << result.error_message << std::endl; }
Attempting to access a table that does not exist:
fluss::Table table; fluss::Result result = conn.GetTable(fluss::TablePath("fluss", "nonexistent"), table); if (!result.Ok()) { if (result.error_code == fluss::ErrorCode::TABLE_NOT_EXIST) { std::cerr << "Table not found" << std::endl; } }
Writing to a partitioned primary key table before creating partitions:
// This will fail if partitions are not created first auto row = table.NewRow(); row.Set("user_id", 1); row.Set("region", "US"); row.Set("score", static_cast<int64_t>(100)); fluss::WriteResult wr; fluss::Result result = writer.Upsert(row, wr); if (!result.Ok()) { if (result.error_code == fluss::ErrorCode::PARTITION_NOT_EXISTS) { std::cerr << "Partition not found, create partitions before writing" << std::endl; } }
SASL credentials are incorrect or the user does not exist:
fluss::Configuration config; config.bootstrap_servers = "127.0.0.1:9123"; config.security_protocol = "sasl"; config.security_sasl_username = "admin"; config.security_sasl_password = "wrong-password"; fluss::Connection conn; fluss::Result result = fluss::Connection::Create(config, conn); if (!result.Ok()) { if (result.error_code == fluss::ErrorCode::AUTHENTICATE_EXCEPTION) { std::cerr << "Authentication failed: " << result.error_message << std::endl; } }
Using incorrect types or column indices when writing:
fluss::GenericRow row; // Setting wrong type for a column will result in an error // when the row is sent to the server row.SetString(0, "not_an_integer"); // Column 0 expects Int fluss::Result result = writer.Append(row); if (!result.Ok()) { std::cerr << "Schema mismatch: " << result.error_message << std::endl; }
Result: Never ignore the return value of operations that return Result.Available() to check connection validity before operations.