Table of Contents

Errors & Events

The stable storage.* error codes every provider maps to, helpers for deciding whether to retry, and the events the library publishes for monitoring.

Error codes

Expected failures come back as a failed Result with a stable code; caller cancellation propagates as OperationCanceledException. Every provider maps its own failures to the same codes:

Code Meaning Typical sources
storage.not_found the item does not exist FTP 550, SFTP no-such-file, HTTP 404
storage.conflict the destination exists, or a condition failed ConflictPolicy.Fail, ETag mismatch
storage.invalid_path the path escapes the mount or is malformed .., rooted paths, rejected names
storage.invalid_content an argument or option is invalid bad octal mode, bad command text
storage.too_large a bounded read or upload exceeded its limit DownloadBytesAsync limits
storage.unsupported the connection cannot do this missing capability, server lacks a command
storage.authentication_failed the credentials were rejected FTP 530, SSH auth, HTTP 401
storage.permission_denied signed in, but not allowed FTP 550/553 permission, SFTP, HTTP 403, local ACL
storage.tls_failure TLS handshake or certificate pin failed untrusted or unpinned certificate
storage.host_key_rejected the SSH host key is not trusted no matching fingerprint or known_hosts entry
storage.connection_failed could not connect DNS failure, refused, unreachable, proxy failure
storage.connection_lost the connection dropped mid-operation FTP 421/426, reset sockets
storage.server_busy rate limited or out of sessions HTTP 429/503, FTP 421, session pool full
storage.quota_exceeded out of space or quota FTP 452/552, HTTP 507, disk full
storage.timeout the operation timed out read/connect timeouts
storage.unavailable the service is unavailable provider outages
storage.partial_failure a multi-step operation stopped halfway restore, staging cleanup, or move-source deletion failed
storage.provider_error anything not classified above carries the exception type (never its message)

StorageErrorInfo (namespace CL.Storage.Errors) helps act on them:

if (StorageErrorInfo.IsTransient(result.Error))              // timeout, unavailable, connection_*, server_busy
{
    if (StorageErrorInfo.TryGetRetryAfter(result.Error, out var wait))
        await Task.Delay(wait);
}

if (StorageErrorInfo.TryGetDetail(result.Error, StorageErrorInfo.FtpReplyKey, out var reply))
    Console.WriteLine($"FTP server replied {reply}");         // also SftpStatusKey, HttpStatusKey

A rejected SSH host key carries presentedFingerprint in Details. Provider response bodies, credentials, and signed query strings are never included. FTP, SFTP, and WebDAV already retry transient failures themselves (see Connections), so a transient error you receive has already been retried.

Events

All events go to the CodeLogic event bus. Publishing never delays or fails the storage operation.

Event Published when
StorageItemWrittenEvent an upload or append committed
StorageItemDeletedEvent an item was deleted
StorageItemCopiedEvent / StorageItemMovedEvent a copy or move within one connection completed
StorageCrossConnectionCopyCompletedEvent / …MoveCompletedEvent a relayed transfer between connections completed (with counts)
StorageDirectoryUploadedEvent / StorageDirectoryDownloadedEvent a local folder transfer completed
StorageConnectionOpenedEvent an FTP or SFTP session opened and authenticated
StorageConnectionLostEvent a session dropped, timed out, or failed TLS and was retired
StorageConnectionRetryEvent a transient failure is being retried (attempt, delay, code)
StorageConnectionHealthChangedEvent a health check found a connection in a new state (and on its first check)
StorageOperationFailedEvent any service operation failed (operation, path, code)
StorageTransferStartedEvent / CompletedEvent / FailedEvent a transfer-queue job changed state
events.Subscribe<StorageConnectionHealthChangedEvent>(e =>
    logger.Warning($"{e.ConnectionId} is now {(e.Healthy ? "healthy" : $"unhealthy ({e.ErrorCode})")}"));

events.Subscribe<StorageOperationFailedEvent>(e =>
{
    if (e.ErrorCode != StorageErrors.NotFoundCode)
        logger.Warning($"{e.Operation} {e.Path} on {e.ConnectionId} failed: {e.ErrorCode}");
});

StorageOperationFailedEvent includes expected failures such as storage.not_found, so filter on ErrorCode. Input rejected before reaching the provider (invalid paths or options) is not reported. Each retry and each retired session is also logged as a warning.