ObjectStoreX.Error (ObjectStoreX v0.2.0)

View Source

Error types and handling for ObjectStoreX.

This module provides comprehensive error handling with descriptive error types, context information, and utilities for retry logic.

Error Types

All ObjectStoreX operations return tagged tuples:

  • {:ok, result} on success
  • {:error, reason} on failure

The reason can be either a simple atom or a tuple with context:

  • :not_found - Object doesn't exist
  • :already_exists - Object exists (create-only mode)
  • :precondition_failed - Conditional operation failed
  • :not_modified - Object not modified (conditional GET)
  • :permission_denied - Insufficient permissions
  • :not_supported - Operation not supported by provider
  • :timeout - Operation timed out
  • :network_error - Network/connection error
  • :invalid_input - Invalid parameters
  • {:unknown, message} - Unknown error with details

Error Context

For detailed error information, errors can include context:

{:error, {:permission_denied, %{
  operation: :put,
  path: "protected/file.txt",
  provider: :s3,
  message: "Access Denied"
}}}

Examples

# Simple error handling
case ObjectStoreX.get(store, "missing.txt") do
  {:ok, data} -> {:ok, data}
  {:error, :not_found} -> {:error, "File not found"}
  {:error, reason} -> {:error, ObjectStoreX.Error.format_error(reason)}
end

# Retry logic
def get_with_retry(store, path, retries \\ 3) do
  case ObjectStoreX.get(store, path) do
    {:ok, data} -> {:ok, data}
    {:error, reason} when retries > 0 ->
      if ObjectStoreX.Error.retryable?(reason) do
        :timer.sleep(1000)
        get_with_retry(store, path, retries - 1)
      else
        {:error, reason}
      end
    {:error, reason} -> {:error, reason}
  end
end

Summary

Functions

Formats an error for display.

Maps a generic error to a specific error reason.

Returns true if the error is retryable.

Creates an error with context information.

Types

detailed_error()

@type detailed_error() :: {error_reason(), error_context()}

error_context()

@type error_context() :: %{
  optional(:operation) => atom(),
  optional(:path) => String.t(),
  optional(:provider) => atom(),
  optional(:message) => String.t()
}

error_reason()

@type error_reason() ::
  :not_found
  | :already_exists
  | :precondition_failed
  | :not_modified
  | :permission_denied
  | :not_supported
  | :timeout
  | :network_error
  | :invalid_input
  | {:unknown, String.t()}

Functions

format_error(error)

@spec format_error(error_reason() | detailed_error() | any()) :: String.t()

Formats an error for display.

Returns a human-readable error message for any error reason.

Examples

iex> ObjectStoreX.Error.format_error(:not_found)
"Object not found"

iex> ObjectStoreX.Error.format_error(:permission_denied)
"Permission denied"

iex> ObjectStoreX.Error.format_error({:unknown, "Connection reset"})
"Unknown error: Connection reset"

iex> ObjectStoreX.Error.format_error({:permission_denied, %{path: "file.txt"}})
"Permission denied"

map_error(msg)

@spec map_error(any()) :: error_reason()

Maps a generic error to a specific error reason.

This is useful for converting string errors from NIFs or exceptions to standardized error atoms.

Examples

iex> ObjectStoreX.Error.map_error("not found")
:not_found

iex> ObjectStoreX.Error.map_error("NotFound: The specified key does not exist")
:not_found

iex> ObjectStoreX.Error.map_error("PermissionDenied: Access Denied")
:permission_denied

iex> ObjectStoreX.Error.map_error("timeout")
:timeout

iex> ObjectStoreX.Error.map_error("something weird")
{:unknown, "something weird"}

retryable?(arg1)

@spec retryable?(error_reason() | detailed_error()) :: boolean()

Returns true if the error is retryable.

Retryable errors are transient and may succeed if retried. Non-retryable errors are permanent and will not succeed on retry.

Retryable Errors

  • :timeout - Operation may succeed on retry
  • :network_error - Network may recover
  • :precondition_failed - For CAS retry with new ETag

Non-Retryable Errors

  • :not_found - Object doesn't exist, retrying won't help
  • :already_exists - Object exists, retrying won't change that
  • :permission_denied - Credentials issue, won't fix on retry
  • :not_supported - Feature not supported, will never work
  • :invalid_input - Bad parameters, won't change on retry

Examples

iex> ObjectStoreX.Error.retryable?(:timeout)
true

iex> ObjectStoreX.Error.retryable?(:network_error)
true

iex> ObjectStoreX.Error.retryable?(:precondition_failed)
true

iex> ObjectStoreX.Error.retryable?(:not_found)
false

iex> ObjectStoreX.Error.retryable?(:permission_denied)
false

iex> ObjectStoreX.Error.retryable?({:timeout, %{path: "file.txt"}})
true

with_context(reason, context)

@spec with_context(error_reason(), error_context()) :: detailed_error()

Creates an error with context information.

Useful for adding operation-specific details to errors.

Examples

iex> ObjectStoreX.Error.with_context(:not_found, %{
...>   operation: :get,
...>   path: "missing.txt",
...>   provider: :s3
...> })
{:not_found, %{operation: :get, path: "missing.txt", provider: :s3}}

iex> ObjectStoreX.Error.with_context(:permission_denied, %{
...>   operation: :put,
...>   path: "protected/file.txt",
...>   message: "Access Denied"
...> })
{:permission_denied, %{operation: :put, path: "protected/file.txt", message: "Access Denied"}}