Skip to main content
Version: 3.4.0

Error Codes

All Client API methods return an int status code. Check the return value against chronolog::CL_SUCCESS (0) before proceeding — any negative value indicates an error. All codes are defined in the chronolog namespace.

#include <client_errcode.h>

Client Error Codes​

CodeValueDescription
CL_SUCCESS0Operation completed successfully
CL_ERR_UNKNOWN-1Generic/unexpected error
CL_ERR_INVALID_ARG-2Invalid argument or parameter
CL_ERR_NOT_EXIST-3Chronicle or Story does not exist
CL_ERR_ACQUIRED-4Resource is acquired and cannot be destroyed
CL_ERR_NOT_ACQUIRED-5Resource is not acquired and cannot be released
CL_ERR_CHRONICLE_EXISTS-6Chronicle with that name already exists
CL_ERR_NO_KEEPERS-7No ChronoKeeper nodes available
CL_ERR_NO_CONNECTION-8No connection to ChronoVisor
CL_ERR_NOT_AUTHORIZED-9Operation not authorized
CL_ERR_NO_PLAYERS-10No ChronoPlayer nodes available
CL_ERR_NOT_READER_MODE-11Client is in WRITER_MODE; read operations unavailable
CL_ERR_QUERY_TIMED_OUT-12Replay query or tail-read RPC timed out
CL_ERR_PROTOCOL_VERSION_MISMATCH-13Client and server disagree on the wire-protocol version
CL_ERR_PARTIAL_RESULT-18Query returned fewer records than it should: for a tail read, events left keeper memory between its two phases; for a replay, a keeper missed its fetch deadline or hit its event cap, or an archive file could not be read. The records that came back are valid
CL_ERR_DURABILITY_UNAVAILABLE-19Durable write: the keeper has no WAL, its WAL failed, it is shutting down, or it lacks the durable RPC. Nothing was stored
CL_ERR_DURABILITY_FULL-20Durable write: the keeper's WAL file system is below its reserve, or the keeper holds more unconfirmed data than durable_cap_mb. Nothing was stored
CL_ERR_DURABILITY_UNKNOWN-21Durable write: the event may be stored. event_time holds its timestamp (see Durability error codes)
Reserved range

Codes -14 through -17 are intentionally unassigned. They are reserved for the ingest acceptance-window and collision-policy work developing in parallel, so that branch and this one never assign the same number to different meanings.

Durability error codes​

Only StoryHandle::log_event(record, options, event_time) with Durability::Durable returns these three codes (see the API reference). Each says whether the event can be stored, which decides what the caller does next.

CodeWas the event stored?CauseWhat to do
CL_ERR_DURABILITY_UNAVAILABLE (-19)NoThe keeper runs with the WAL off; its WAL hit a write or fdatasync error and stays failed until the keeper restarts; the keeper is shutting down; or the keeper is older than 3.4.0 and lacks the durable RPC.Retrying will not help until an operator acts: turn the WAL on, restart the keeper whose WAL failed (its log has a critical line), wait until a keeper that is shutting down has been restarted, or upgrade the keepers. The client returns it at once, with no retry. If losing the event on a keeper crash is acceptable, write it with Durability::Accepted; the client never does that by itself.
CL_ERR_DURABILITY_FULL (-20)NoThe free space on the keeper's WAL file system is below the reserve (the larger of reserve_min_bytes and reserve_percent of the file system), or the keeper holds more than durable_cap_mb of chunks the grapher has not confirmed. Usually ChronoGrapher is behind or down, so the keeper can release nothing.Retry later, with a backoff. The keeper samples its free space every 100 ms and admits durable writes again once the space is back above the reserve and the unconfirmed chunks are down to three quarters of durable_cap_mb. A chunk stops counting when the grapher confirms it, even while the tail index still holds it.
CL_ERR_DURABILITY_UNKNOWN (-21)MaybeNo attempt got an answer within timeout_ms, after attempts attempts. Also returned when an earlier attempt went unanswered and a later one got -19, -20 or found no durable RPC, and when the story's keeper list became empty between attempts: the unanswered attempt may have reached a keeper that stored the event. And when the keeper answers -21 itself: its WAL failed while writing the event and could not erase the bytes it wrote, so they may be on disk.The caller decides. A new log_event call creates a second event, with a new timestamp and index, so if both are stored a reader sees two events. To make that safe, put an identifier of your own in the record and drop repeats when reading. To find out whether the event was stored, use event_time; see below.

A durable write that gets -19 or -20 was not ingested either: the event does not appear in playback or in a replay. With -21 it may appear, once.

When a group's write or fdatasync fails, the keeper overwrites the group's bytes with zeros and syncs them before it answers, so a keeper restarted on the same boot does not read the group back. Every writer in the group then gets -19. When the keeper cannot erase the group, it answers -21, because the records may be on disk. It cannot tell a write that failed before writing anything from one that wrote part of the group, so a failed write whose erase also fails gets -21 as well. If the keeper crashes after the failed write or sync, before the zeros are synced, the writers got no answer at all and see -21. If a retry reaches the restarted keeper within the client's retry window (3 attempts of 10 s by default), the keeper may have read the group back from the page cache, the retry finds the event, and the writer sees CL_SUCCESS, although the group may not be on the disk.

Checking whether a -21 event was stored​

With -21, event_time is the event's timestamp, and with every other failure it is 0. The timestamp is the first part of the event's identity. To check whether the event was stored, wait until a restarted keeper has replayed its WAL, then replay the story over a short range around event_time with ReplayStory and look for a record that matches yours. Put an identifier of your own in the record to make the match certain. If a matching record is there, the event was stored; do not write it again. If none is there after the keepers have recovered, write it again with a new log_event call.

Retries and shutdown​

A keeper that is shutting down (SIGTERM) takes no new write once its shutdown starts, after it has unregistered from the visor: it answers a durable write -19 and an accepted write CL_ERR_UNKNOWN (-1), and stores nothing. Durable writes it had already queued are synced and answered as usual. If an earlier attempt of the same write went unanswered, the writer sees -21 instead of -19, as with any -19.

This lasts until the keeper deletes its recording service, late in the shutdown. That deregisters all its RPCs, including the tail read the client uses as a probe, so a durable write is then an unanswered attempt as against a dead keeper: the client waits, retries, and returns -21. An accepted write still gets -1.

The client reads "RPC not registered" from a keeper as "this keeper lacks the durable RPC" only while that keeper has never answered a durable call from this client, and only if the keeper still answers a tail read of no story, an RPC every keeper since 3.1 defines. Over ofi+sockets a call to a keeper that is down fails with the same error, but then the tail read gets no answer either, so the call is an unanswered attempt and is retried, as is every call after the keeper's first durable answer. A call that fails at once is not retried at once: the client waits until the attempt's timeout_ms has passed, so the attempts are timeout_ms apart and a keeper restarted at the same address can answer. If it does not come back in time, the writer gets -21 with the event's timestamp.

Error Handling​

Use to_string_client(int) to get the error name as a string for logging:

int ret = client.Connect();
if (ret != chronolog::CL_SUCCESS) {
std::cerr << "Connect failed: " << chronolog::to_string_client(ret) << "\n";
return 1;
}