Module Error

Centralized error handling for the Requests library using Eio.Io exceptions.

This module follows the Eio.Io exception pattern for structured error handling, providing granular error types and query functions for smart retry logic.

Usage

Errors are raised using the Eio.Io pattern:

  raise (Error.err (Error.Timeout { operation = "connect"; duration = Some 30.0 }))

To catch and handle errors:

  try
    (* ... HTTP request ... *)
  with
  | Eio.Io (Error.E e, _) when Error.is_retryable e ->
      (* Retry the request *)
  | Eio.Io (Error.E e, _) ->
      Printf.eprintf "Request failed: %s\n" (Error.to_string e)
val src : Logs.Src.t

Log source for error reporting

Error Type

Granular error variants with contextual information. Each variant contains a record with relevant details.

type error =
  1. | Timeout of {
    1. operation : string;
    2. duration : float option;
    }
  2. | Too_many_redirects of {
    1. url : string;
    2. count : int;
    3. max : int;
    }
  3. | Invalid_redirect of {
    1. url : string;
    2. reason : string;
    }
  4. | Http_error of {
    1. url : string;
    2. status : int;
    3. reason : string;
    4. body_preview : string option;
    5. headers : (string * string) list;
    }
  5. | Authentication_failed of {
    1. url : string;
    2. reason : string;
    }
  6. | Dns_resolution_failed of {
    1. hostname : string;
    }
  7. | Tcp_connect_failed of {
    1. host : string;
    2. port : int;
    3. reason : string;
    }
  8. | Tls_handshake_failed of {
    1. host : string;
    2. reason : string;
    }
  9. | Invalid_header of {
    1. name : string;
    2. reason : string;
    }
  10. | Body_too_large of {
    1. limit : int64;
    2. actual : int64 option;
    }
  11. | Headers_too_large of {
    1. limit : int;
    2. actual : int;
    }
  12. | Decompression_bomb of {
    1. limit : int64;
    2. ratio : float;
    }
  13. | Content_length_mismatch of {
    1. expected : int64;
    2. actual : int64;
    }
  14. | Insecure_auth of {
    1. url : string;
    2. auth_type : string;
    }
    (*

    Per RFC 7617 Section 4 and RFC 6750 Section 5.1: Basic, Bearer, and Digest authentication over unencrypted HTTP exposes credentials to eavesdropping. Raised when attempting to use these auth methods over HTTP without explicit opt-out.

    *)
  15. | Json_parse_error of {
    1. body_preview : string;
    2. reason : string;
    }
  16. | Json_encode_error of {
    1. reason : string;
    }
  17. | Proxy_error of {
    1. host : string;
    2. reason : string;
    }
  18. | Encoding_error of {
    1. encoding : string;
    2. reason : string;
    }
  19. | Invalid_url of {
    1. url : string;
    2. reason : string;
    }
  20. | Invalid_request of {
    1. reason : string;
    }
  21. | Oauth_error of {
    1. error_code : string;
    2. description : string option;
    3. uri : string option;
    }
    (*

    OAuth 2.0 error response from authorization server. Per RFC 6749 Section 5.2.

    *)
  22. | Token_refresh_failed of {
    1. reason : string;
    }
    (*

    Token refresh operation failed.

    *)
  23. | Token_expired
    (*

    Access token has expired and no refresh token is available.

    *)
  24. | H2_protocol_error of {
    1. code : int32;
    2. message : string;
    }
    (*

    HTTP/2 connection error per RFC 9113 Section 5.4.1. Error codes are defined in RFC 9113 Section 7.

    *)
  25. | H2_stream_error of {
    1. stream_id : int32;
    2. code : int32;
    3. message : string;
    }
    (*

    HTTP/2 stream error per RFC 9113 Section 5.4.2.

    *)
  26. | H2_flow_control_error of {
    1. stream_id : int32 option;
    }
    (*

    Flow control window exceeded per RFC 9113 Section 5.2.

    *)
  27. | H2_compression_error of {
    1. message : string;
    }
    (*

    HPACK decompression failed per RFC 7541.

    *)
  28. | H2_settings_timeout
    (*

    SETTINGS acknowledgment timeout per RFC 9113 Section 6.5.3.

    *)
  29. | H2_goaway of {
    1. last_stream_id : int32;
    2. code : int32;
    3. debug : string;
    }
    (*

    Server sent GOAWAY frame per RFC 9113 Section 6.8.

    *)
  30. | H2_frame_error of {
    1. frame_type : int;
    2. message : string;
    }
    (*

    Invalid frame received per RFC 9113 Section 4-6.

    *)
  31. | H2_header_validation_error of {
    1. message : string;
    }
    (*

    HTTP/2 header validation failed per RFC 9113 Section 8.2-8.3.

    *)

Eio.Exn Integration

type Eio.Exn.err +=
  1. | E of error

Extension of Eio.Exn.err for Requests errors

val err : error -> exn

Create an Eio exception from an error. Usage: raise (err (Timeout { operation = "read"; duration = Some 5.0 }))

URL and Credential Sanitization

val sanitize_url : string -> string

Remove userinfo (username:password) from a URL for safe logging

val sanitize_headers : (string * string) list -> (string * string) list

Redact sensitive headers (Authorization, Cookie, etc.) for safe logging. Takes and returns a list of (name, value) pairs.

val is_sensitive_header : string -> bool

Check if a header name is sensitive (case-insensitive)

Pretty Printing

val pp_error : Format.formatter -> error -> unit

Pretty printer for error values

Query Functions

These functions enable smart error handling without pattern matching.

val is_timeout : error -> bool

Returns true if the error is a timeout

val is_dns : error -> bool

Returns true if the error is a DNS resolution failure

val is_tls : error -> bool

Returns true if the error is a TLS handshake failure

val is_connection : error -> bool

Returns true if the error is any connection-related failure (DNS, TCP connect, or TLS handshake)

val is_http_error : error -> bool

Returns true if the error is an HTTP response error

val is_client_error : error -> bool

Returns true if the error is a client error (4xx status or similar)

val is_server_error : error -> bool

Returns true if the error is a server error (5xx status)

val is_retryable : error -> bool

Returns true if the error is typically retryable. Retryable errors include: timeouts, connection errors, and certain HTTP status codes (408, 429, 500, 502, 503, 504)

val is_security_error : error -> bool

Returns true if the error is security-related (header injection, body too large, decompression bomb, etc.)

val is_json_error : error -> bool

Returns true if the error is a JSON parsing or encoding error

val is_oauth_error : error -> bool

Returns true if the error is an OAuth-related error (Oauth_error, Token_refresh_failed, Token_expired)

Error Extraction

val of_eio_exn : exn -> error option

Extract error from an Eio.Io exception, if it's a Requests error

HTTP Status Helpers

val get_http_status : error -> int option

Get the HTTP status code from an error, if applicable

val get_url : error -> string option

Get the URL associated with an error, if applicable

String Conversion

val to_string : error -> string

Convert error to human-readable string

Convenience Constructors

These functions provide a more concise way to create error exceptions compared to the verbose err (Error_type { field = value; ... }) pattern.

Example:

  (* Instead of: *)
  raise (err (Invalid_request { reason = "missing host" }))

  (* Use: *)
  raise (invalid_request ~reason:"missing host")
val invalid_request : reason:string -> exn

Create an Invalid_request exception

val invalid_redirect : url:string -> reason:string -> exn

Create an Invalid_redirect exception

val invalid_url : url:string -> reason:string -> exn

Create an Invalid_url exception

val timeout : operation:string -> ?duration:float -> unit -> exn

Create a Timeout exception

val body_too_large : limit:int64 -> ?actual:int64 -> unit -> exn

Create a Body_too_large exception

val headers_too_large : limit:int -> actual:int -> exn

Create a Headers_too_large exception

val proxy_error : host:string -> reason:string -> exn

Create a Proxy_error exception

val tls_handshake_failed : host:string -> reason:string -> exn

Create a Tls_handshake_failed exception

val tcp_connect_failed : host:string -> port:int -> reason:string -> exn

Create a Tcp_connect_failed exception

Format String Constructors

These functions accept printf-style format strings for the reason field, making error construction more concise when messages need interpolation.

Example:

  (* Instead of: *)
  raise (Error.err (Error.Invalid_request {
    reason = Printf.sprintf "Invalid status code: %s" code_str
  }))

  (* Use: *)
  raise (Error.invalid_requestf "Invalid status code: %s" code_str)
val invalid_requestf : ('a, unit, string, exn) format4 -> 'a

Create an Invalid_request exception with a format string

val invalid_redirectf : url:string -> ('a, unit, string, exn) format4 -> 'a

Create an Invalid_redirect exception with a format string

val invalid_urlf : url:string -> ('a, unit, string, exn) format4 -> 'a

Create an Invalid_url exception with a format string

val proxy_errorf : host:string -> ('a, unit, string, exn) format4 -> 'a

Create a Proxy_error exception with a format string

val tls_handshake_failedf : host:string -> ('a, unit, string, exn) format4 -> 'a

Create a Tls_handshake_failed exception with a format string

val tcp_connect_failedf : host:string -> port:int -> ('a, unit, string, exn) format4 -> 'a

Create a Tcp_connect_failed exception with a format string

OAuth Error Constructors

val oauth_error : error_code:string -> ?description:string -> ?uri:string -> unit -> exn

Create an Oauth_error exception

val token_refresh_failed : reason:string -> exn

Create a Token_refresh_failed exception

val token_expired : unit -> exn

Create a Token_expired exception

HTTP/2 Error Query Functions

Query functions for HTTP/2 specific errors per RFC 9113.

val is_h2_error : error -> bool

Returns true if the error is any HTTP/2 protocol error

val is_h2_connection_error : error -> bool

Returns true if the error is an HTTP/2 connection-level error. Connection errors terminate the entire HTTP/2 connection.

val is_h2_stream_error : error -> bool

Returns true if the error is an HTTP/2 stream-level error. Stream errors only affect a single stream.

val is_h2_retryable : error -> bool

Returns true if the HTTP/2 error is typically retryable. Retryable errors include:

  • GOAWAY with NO_ERROR (graceful shutdown)
  • REFUSED_STREAM (server didn't process the request)
  • ENHANCE_YOUR_CALM (after backoff)
val get_h2_error_code : error -> int32 option

Get the HTTP/2 error code from an error, if applicable. Error codes are defined in RFC 9113 Section 7.

val get_h2_stream_id : error -> int32 option

Get the stream ID associated with an HTTP/2 error, if applicable.

HTTP/2 Error Constructors

Convenience constructors for HTTP/2 errors per RFC 9113 Section 7.

val h2_protocol_error : code:int32 -> message:string -> exn

Create an H2_protocol_error exception

val h2_stream_error : stream_id:int32 -> code:int32 -> message:string -> exn

Create an H2_stream_error exception

val h2_flow_control_error : ?stream_id:int32 -> unit -> exn

Create an H2_flow_control_error exception. If stream_id is provided, it's a stream-level error; otherwise, it's a connection-level error.

val h2_compression_error : message:string -> exn

Create an H2_compression_error exception

val h2_settings_timeout : unit -> exn

Create an H2_settings_timeout exception

val h2_goaway : last_stream_id:int32 -> code:int32 -> debug:string -> exn

Create an H2_goaway exception

val h2_frame_error : frame_type:int -> message:string -> exn

Create an H2_frame_error exception

val h2_header_validation_error : message:string -> exn

Create an H2_header_validation_error exception

HTTP/2 Error Code Names

val h2_error_code_name : int32 -> string

h2_error_code_name code returns the name of an HTTP/2 error code. Per RFC 9113 Section 7:

  • 0x0: NO_ERROR
  • 0x1: PROTOCOL_ERROR
  • 0x2: INTERNAL_ERROR
  • 0x3: FLOW_CONTROL_ERROR
  • 0x4: SETTINGS_TIMEOUT
  • 0x5: STREAM_CLOSED
  • 0x6: FRAME_SIZE_ERROR
  • 0x7: REFUSED_STREAM
  • 0x8: CANCEL
  • 0x9: COMPRESSION_ERROR
  • 0xa: CONNECT_ERROR
  • 0xb: ENHANCE_YOUR_CALM
  • 0xc: INADEQUATE_SECURITY
  • 0xd: HTTP_1_1_REQUIRED