Error Serialization Reference

View Source

This document provides detailed information about how ErrorMessage handles serialization of error messages to different formats.

String Serialization

ErrorMessage implements the String.Chars protocol, which allows error messages to be converted to strings using to_string/1 or string interpolation.

Basic Format

The string representation of an error message follows this format:

  • For errors without details: "#{code} - #{message}"
  • For errors with details: "#{code} - #{message}\nDetails: \n#{inspect(details, pretty: true)}"

Examples

# Error without details
error = ErrorMessage.not_found("User not found")
to_string(error)  # "not_found - User not found"

# Error with details
error = ErrorMessage.internal_server_error("Database error", %{table: "users", reason: :connection_lost})
to_string(error)
# "internal_server_error - Database error
# Details: 
# %{reason: :connection_lost, table: "users"}"

# String interpolation
"Error occurred: #{error}"
# "Error occurred: internal_server_error - Database error
# Details: 
# %{reason: :connection_lost, table: "users"}"

JSON Serialization

ErrorMessage provides the to_jsonable_map/1 function to convert error messages to maps suitable for JSON serialization.

Basic Format

The JSON representation of an error message follows this format:

{
  "code": "error_code",
  "message": "Error message",
  "details": { ... }
}

If a request ID is available in the Logger metadata, it will be included:

{
  "code": "error_code",
  "message": "Error message",
  "details": { ... },
  "request_id": "FzMx0iBDvDDJ-GkAAAfh"
}

Data Type Handling

The ErrorMessage.Serializer module includes the ensure_json_serializable/1 function, which handles conversion of Elixir-specific data types to JSON-compatible formats:

Elixir TypeJSON Representation
DateISO 8601 date string
TimeISO 8601 time string
DateTimeISO 8601 datetime string
NaiveDateTimeISO 8601 datetime string
Struct{"struct": "StructName", "data": {...}}
TupleArray
PIDString representation (with registered name if available)
Function{"module": "Module", "function": "function_name", "arity": n}
ListArray with each element converted recursively
MapObject with each value converted recursively
Other typesPassed through as-is

Examples

# Basic error
error = ErrorMessage.not_found("User not found", %{user_id: 123})
ErrorMessage.to_jsonable_map(error)
# %{code: :not_found, message: "User not found", details: %{user_id: 123}}

# Error with complex details
error = ErrorMessage.bad_request("Invalid data", %{
  date: ~D[2023-01-15],
  time: ~T[14:30:00],
  callback: &String.length/1,
  user: %UserStruct{name: "John", created_at: ~N[2023-01-01 00:00:00]}
})

ErrorMessage.to_jsonable_map(error)
# %{
#   code: :bad_request,
#   message: "Invalid data",
#   details: %{
#     date: "2023-01-15",
#     time: "14:30:00",
#     callback: %{module: "Elixir.String", function: "length", arity: 1},
#     user: %{
#       struct: "UserStruct",
#       data: %{
#         name: "John",
#         created_at: "2023-01-01T00:00:00"
#       }
#     }
#   }
# }

Direct JSON Encoding

If the Jason library is available, ErrorMessage automatically implements the Jason.Encoder protocol, allowing error messages to be directly encoded to JSON:

# With Jason available
Jason.encode!(ErrorMessage.not_found("User not found"))
# "{\"code\":\"not_found\",\"message\":\"User not found\"}"

# With details
Jason.encode!(ErrorMessage.not_found("User not found", %{user_id: 123}))
# "{\"code\":\"not_found\",\"message\":\"User not found\",\"details\":{\"user_id\":123}}"

Note that when using Jason.encode!/1 directly on an %ErrorMessage{} struct, the serialization of complex data types in the details field may not be handled as thoroughly as when using ErrorMessage.to_jsonable_map/1 first. For the most reliable JSON serialization, especially with complex data structures, use:

error
|> ErrorMessage.to_jsonable_map()
|> Jason.encode!()

Integration with Phoenix

When using ErrorMessage with Phoenix, you can easily render error messages as JSON responses:

defmodule MyApp.ErrorView do
  use MyApp, :view
  
  def render("error.json", %{error: error}) do
    ErrorMessage.to_jsonable_map(error)
  end
end

This will produce JSON responses with the appropriate structure and data type handling.