···11(** Authentication for the Zulip API.
2233 This module handles authentication credentials for connecting to a Zulip server.
44- @raise Eio.Io with [Zulip_types.E error] on authentication/config errors *)
44+ @raise Eio.Io with [Error.E error] on authentication/config errors *)
5566type t
77
+3-2
lib/zulip/channels.ml
···2828 match Encode.from_json response_codec json with
2929 | Ok channels -> channels
3030 | Error msg ->
3131- let err = Zulip_types.create_error ~code:(Other "json_parse") ~msg () in
3232- raise (Eio.Exn.add_context (Zulip_types.err err) "parsing channels list")
3131+ Error.raise_with_context
3232+ (Error.make ~code:(Other "json_parse") ~message:msg ())
3333+ "parsing channels list"
33343435(* Request types with jsont codecs *)
3536module Subscribe_request = struct
+1-1
lib/zulip/channels.mli
···11(** Channel (stream) operations for the Zulip API.
2233- All functions raise [Eio.Io] with [Zulip_types.E error] on failure.
33+ All functions raise [Eio.Io] with [Error.E error] on failure.
44 Context is automatically added indicating the operation being performed. *)
5566val create_channel : Client.t -> Channel.t -> unit
+25-32
lib/zulip/client.ml
···100100 | Error e ->
101101 let msg = Jsont.Error.to_string e in
102102 Log.err (fun m -> m "JSON parse error: %s" msg);
103103- let err =
104104- Zulip_types.create_error ~code:(Other "json_parse") ~msg ()
105105- in
106106- raise (Eio.Exn.add_context (Zulip_types.err err) "%s %s" (method_to_string method_) path)
103103+ Error.raise_with_context
104104+ (Error.make ~code:(Other "json_parse") ~message:msg ())
105105+ "%s %s" (method_to_string method_) path
107106 in
108107109108 (* Check for Zulip error response *)
···117116 | Some (Jsont.String (s, _)) -> s
118117 | _ -> "Unknown error"
119118 in
120120- let code =
119119+ let code : Error.code =
121120 match List.assoc_opt "code" assoc with
122122- | Some (Jsont.String (s, _)) ->
123123- (match s with
124124- | "INVALID_API_KEY" -> Zulip_types.Invalid_api_key
125125- | "REQUEST_VARIABLE_MISSING" -> Zulip_types.Request_variable_missing
126126- | "BAD_REQUEST" -> Zulip_types.Bad_request
127127- | "USER_DEACTIVATED" -> Zulip_types.User_deactivated
128128- | "REALM_DEACTIVATED" -> Zulip_types.Realm_deactivated
129129- | "RATE_LIMIT_HIT" -> Zulip_types.Rate_limit_hit
130130- | s -> Zulip_types.Other s)
131131- | _ -> Zulip_types.Other "unknown"
121121+ | Some (Jsont.String (s, _)) -> (
122122+ match s with
123123+ | "INVALID_API_KEY" -> Invalid_api_key
124124+ | "REQUEST_VARIABLE_MISSING" -> Request_variable_missing
125125+ | "BAD_REQUEST" -> Bad_request
126126+ | "USER_DEACTIVATED" -> User_deactivated
127127+ | "REALM_DEACTIVATED" -> Realm_deactivated
128128+ | "RATE_LIMIT_HIT" -> Rate_limit_hit
129129+ | s -> Other s)
130130+ | _ -> Other "unknown"
132131 in
133133- (* Extract extra fields *)
134132 let extra =
135133 List.filter
136134 (fun (k, _) -> k <> "code" && k <> "msg" && k <> "result")
137135 assoc
138136 in
139139- Log.warn (fun m ->
140140- m "API error: %s (code: %a)" msg Zulip_types.pp_error_code code);
141141- let err = Zulip_types.create_error ~code ~msg ~extra () in
142142- raise (Eio.Exn.add_context (Zulip_types.err err) "%s %s" (method_to_string method_) path)
137137+ Log.warn (fun m -> m "API error: %s (code: %a)" msg Error.pp_code code);
138138+ Error.raise_with_context
139139+ (Error.make ~code ~message:msg ~extra ())
140140+ "%s %s" (method_to_string method_) path
143141 | _ ->
144142 if status >= 200 && status < 300 then json
145143 else (
146144 Log.warn (fun m -> m "HTTP error: %d" status);
147147- let err =
148148- Zulip_types.create_error
149149- ~code:(Zulip_types.Other (string_of_int status))
150150- ~msg:("HTTP error: " ^ string_of_int status)
151151- ()
152152- in
153153- raise (Eio.Exn.add_context (Zulip_types.err err) "%s %s" (method_to_string method_) path)))
145145+ Error.raise_with_context
146146+ (Error.make ~code:(Other (string_of_int status))
147147+ ~message:("HTTP error: " ^ string_of_int status) ())
148148+ "%s %s" (method_to_string method_) path))
154149 | _ ->
155150 if status >= 200 && status < 300 then json
156151 else (
157152 Log.err (fun m -> m "Invalid JSON response");
158158- let err =
159159- Zulip_types.create_error ~code:(Zulip_types.Other "json_parse")
160160- ~msg:"Invalid JSON response" ()
161161- in
162162- raise (Eio.Exn.add_context (Zulip_types.err err) "%s %s" (method_to_string method_) path))
153153+ Error.raise_with_context
154154+ (Error.make ~code:(Other "json_parse") ~message:"Invalid JSON response" ())
155155+ "%s %s" (method_to_string method_) path)
163156164157let pp fmt t =
165158 Format.fprintf fmt "Client(server=%s)" (Auth.server_url t.auth)
+4-4
lib/zulip/client.mli
···2233 This module provides the low-level HTTP client for communicating with
44 the Zulip API. All API errors are raised as [Eio.Io] exceptions with
55- [Zulip_types.E] error codes, following the Eio error pattern.
55+ [Error.E] error codes, following the Eio error pattern.
6677- @raise Eio.Io with [Zulip_types.E error] for API errors *)
77+ @raise Eio.Io with [Error.E error] for API errors *)
8899type t
1010(** Type representing a Zulip HTTP client *)
···3939 ?body:string ->
4040 ?content_type:string ->
4141 unit ->
4242- Zulip_types.json
4242+ Jsont.json
4343(** Make an HTTP request to the Zulip API.
4444 @param content_type Optional Content-Type header
4545 (default: application/x-www-form-urlencoded for POST/PUT, none for GET/DELETE)
4646- @raise Eio.Io with [Zulip_types.E error] on API errors. The exception
4646+ @raise Eio.Io with [Error.E error] on API errors. The exception
4747 includes context about the request method and path. *)
48484949val pp : Format.formatter -> t -> unit
+93
lib/zulip/error.ml
···11+type code =
22+ | Invalid_api_key
33+ | Request_variable_missing
44+ | Bad_request
55+ | User_deactivated
66+ | Realm_deactivated
77+ | Rate_limit_hit
88+ | Other of string
99+1010+type t = {
1111+ code : code;
1212+ message : string;
1313+ extra : (string * Jsont.json) list;
1414+}
1515+1616+type Eio.Exn.err += E of t
1717+1818+let pp_code fmt = function
1919+ | Invalid_api_key -> Format.fprintf fmt "Invalid_api_key"
2020+ | Request_variable_missing -> Format.fprintf fmt "Request_variable_missing"
2121+ | Bad_request -> Format.fprintf fmt "Bad_request"
2222+ | User_deactivated -> Format.fprintf fmt "User_deactivated"
2323+ | Realm_deactivated -> Format.fprintf fmt "Realm_deactivated"
2424+ | Rate_limit_hit -> Format.fprintf fmt "Rate_limit_hit"
2525+ | Other s -> Format.fprintf fmt "Other(%s)" s
2626+2727+let pp fmt t = Format.fprintf fmt "%a: %s" pp_code t.code t.message
2828+2929+let () =
3030+ Eio.Exn.register_pp (fun f -> function
3131+ | E e ->
3232+ Format.fprintf f "Zulip %a" pp e;
3333+ true
3434+ | _ -> false)
3535+3636+let code_to_api_string = function
3737+ | Invalid_api_key -> "INVALID_API_KEY"
3838+ | Request_variable_missing -> "REQUEST_VARIABLE_MISSING"
3939+ | Bad_request -> "BAD_REQUEST"
4040+ | User_deactivated -> "USER_DEACTIVATED"
4141+ | Realm_deactivated -> "REALM_DEACTIVATED"
4242+ | Rate_limit_hit -> "RATE_LIMIT_HIT"
4343+ | Other s -> s
4444+4545+let code_of_api_string = function
4646+ | "INVALID_API_KEY" -> Invalid_api_key
4747+ | "REQUEST_VARIABLE_MISSING" -> Request_variable_missing
4848+ | "BAD_REQUEST" -> Bad_request
4949+ | "USER_DEACTIVATED" -> User_deactivated
5050+ | "REALM_DEACTIVATED" -> Realm_deactivated
5151+ | "RATE_LIMIT_HIT" -> Rate_limit_hit
5252+ | s -> Other s
5353+5454+let make ~code ~message ?(extra = []) () = { code; message; extra }
5555+let code t = t.code
5656+let message t = t.message
5757+let extra t = t.extra
5858+let raise e = Stdlib.raise (Eio.Exn.create (E e))
5959+6060+let raise_with_context e fmt =
6161+ Format.kasprintf (fun context ->
6262+ Stdlib.raise (Eio.Exn.add_context (Eio.Exn.create (E e)) "%s" context))
6363+ fmt
6464+6565+let code_jsont =
6666+ let of_string s = Ok (code_of_api_string s) in
6767+ Jsont.of_of_string ~kind:"ErrorCode" of_string ~enc:code_to_api_string
6868+6969+let jsont =
7070+ let kind = "ZulipError" in
7171+ let make' code msg = { code = code_of_api_string code; message = msg; extra = [] } in
7272+ let code' t = code_to_api_string t.code in
7373+ let msg t = t.message in
7474+ Jsont.Object.(
7575+ map ~kind make'
7676+ |> mem "code" Jsont.string ~enc:code'
7777+ |> mem "msg" Jsont.string ~enc:msg
7878+ |> finish)
7979+8080+let of_json json =
8181+ match Encode.from_json jsont json with
8282+ | Ok err -> (
8383+ match json with
8484+ | Jsont.Object (fields, _) ->
8585+ let assoc = List.map (fun ((k, _), v) -> (k, v)) fields in
8686+ let extra =
8787+ List.filter
8888+ (fun (k, _) -> k <> "code" && k <> "msg" && k <> "result")
8989+ assoc
9090+ in
9191+ Some { err with extra }
9292+ | _ -> Some err)
9393+ | Error _ -> None
+79
lib/zulip/error.mli
···11+(** Zulip API error handling.
22+33+ This module defines protocol-level errors for the Zulip API,
44+ following the Eio error pattern for context-aware error handling.
55+66+ Errors are raised as [Eio.Io] exceptions:
77+ {[
88+ try
99+ Zulip.Messages.send client msg
1010+ with
1111+ | Eio.Io (Zulip.Error.E { code = Invalid_api_key; message; _ }, _) ->
1212+ Printf.eprintf "Authentication failed: %s\n" message
1313+ | Eio.Io (Zulip.Error.E err, _) ->
1414+ Printf.eprintf "API error: %a\n" Zulip.Error.pp err
1515+ ]} *)
1616+1717+(** {1 Error Codes}
1818+1919+ These error codes correspond to the error codes returned by the Zulip API
2020+ in the "code" field of error responses. *)
2121+2222+type code =
2323+ | Invalid_api_key (** Authentication failure - invalid API key *)
2424+ | Request_variable_missing (** Required parameter is missing *)
2525+ | Bad_request (** Malformed request *)
2626+ | User_deactivated (** User account has been deactivated *)
2727+ | Realm_deactivated (** Organization (realm) has been deactivated *)
2828+ | Rate_limit_hit (** API rate limit exceeded *)
2929+ | Other of string (** Other/unknown error code *)
3030+3131+(** {1 Error Type} *)
3232+3333+type t = {
3434+ code : code; (** The error code from the API *)
3535+ message : string; (** Human-readable error message *)
3636+ extra : (string * Jsont.json) list; (** Additional fields from the error response *)
3737+}
3838+(** The protocol-level error type. *)
3939+4040+(** {1 Eio Integration} *)
4141+4242+type Eio.Exn.err += E of t
4343+(** Extend [Eio.Exn.err] with Zulip protocol errors. *)
4444+4545+val raise : t -> 'a
4646+(** [raise e] raises an [Eio.Io] exception for error [e].
4747+ Equivalent to [Stdlib.raise (Eio.Exn.create (E e))]. *)
4848+4949+val raise_with_context : t -> ('a, Format.formatter, unit, 'b) format4 -> 'a
5050+(** [raise_with_context e fmt ...] raises an [Eio.Io] exception with context.
5151+ Equivalent to [Stdlib.raise (Eio.Exn.add_context (Eio.Exn.create (E e)) fmt ...)]. *)
5252+5353+(** {1 Error Construction} *)
5454+5555+val make : code:code -> message:string -> ?extra:(string * Jsont.json) list -> unit -> t
5656+(** [make ~code ~message ?extra ()] creates an error value. *)
5757+5858+(** {1 Accessors} *)
5959+6060+val code : t -> code
6161+val message : t -> string
6262+val extra : t -> (string * Jsont.json) list
6363+6464+(** {1 Pretty Printing} *)
6565+6666+val pp_code : Format.formatter -> code -> unit
6767+val pp : Format.formatter -> t -> unit
6868+6969+(** {1 JSON Parsing} *)
7070+7171+val code_jsont : code Jsont.t
7272+(** Jsont codec for error codes. *)
7373+7474+val jsont : t Jsont.t
7575+(** Jsont codec for errors. *)
7676+7777+val of_json : Jsont.json -> t option
7878+(** [of_json json] attempts to parse a Zulip API error response.
7979+ Returns [None] if the JSON does not represent an error. *)
+1-1
lib/zulip/event.ml
···11type t = {
22 id : int;
33 type_ : Event_type.t;
44- data : Zulip_types.json;
44+ data : Jsont.json;
55}
6677let id t = t.id
+1-1
lib/zulip/event.mli
···7788val id : t -> int
99val type_ : t -> Event_type.t
1010-val data : t -> Zulip_types.json
1010+val data : t -> Jsont.json
11111212(** Jsont codec for event *)
1313val jsont : t Jsont.t
+6-7
lib/zulip/event_queue.ml
···4848 match Encode.from_json Register_response.codec json with
4949 | Ok response -> { id = response.queue_id }
5050 | Error msg ->
5151- let err = Zulip_types.create_error ~code:(Other "json_parse") ~msg () in
5252- raise
5353- (Eio.Exn.add_context (Zulip_types.err err) "parsing register response")
5151+ Error.raise_with_context
5252+ (Error.make ~code:(Other "json_parse") ~message:msg ())
5353+ "parsing register response"
54545555let id t = t.id
5656···105105 response.events
106106 | Error msg ->
107107 Log.warn (fun m -> m "Failed to parse events response: %s" msg);
108108- let err = Zulip_types.create_error ~code:(Other "json_parse") ~msg () in
109109- raise
110110- (Eio.Exn.add_context (Zulip_types.err err) "parsing events from queue %s"
111111- t.id)
108108+ Error.raise_with_context
109109+ (Error.make ~code:(Other "json_parse") ~message:msg ())
110110+ "parsing events from queue %s" t.id
112111113112let delete t client =
114113 let params = [ ("queue_id", t.id) ] in
+1-1
lib/zulip/event_queue.mli
···11(** Event queue for receiving Zulip events in real-time.
2233- All functions raise [Eio.Io] with [Zulip_types.E error] on failure. *)
33+ All functions raise [Eio.Io] with [Error.E error] on failure. *)
4455type t
66
+6-9
lib/zulip/messages.ml
···99 match Encode.from_json Message_response.jsont response with
1010 | Ok msg_response -> msg_response
1111 | Error msg ->
1212- let err =
1313- Zulip_types.create_error ~code:(Other "json_parse") ~msg ()
1414- in
1515- raise (Eio.Exn.add_context (Zulip_types.err err) "parsing message response")
1212+ Error.raise_with_context
1313+ (Error.make ~code:(Other "json_parse") ~message:msg ())
1414+ "parsing message response"
16151716let edit client ~message_id ?content ?topic () =
1817 let params =
···77767877let upload_file _client ~filename:_ =
7978 (* TODO: Implement file upload using multipart/form-data *)
8080- let err =
8181- Zulip_types.create_error ~code:(Other "not_implemented")
8282- ~msg:"File upload not yet implemented" ()
8383- in
8484- raise (Zulip_types.err err)
7979+ Error.raise
8080+ (Error.make ~code:(Other "not_implemented")
8181+ ~message:"File upload not yet implemented" ())
+3-3
lib/zulip/messages.mli
···11(** Message operations for the Zulip API.
2233- All functions raise [Eio.Io] with [Zulip_types.E error] on failure.
33+ All functions raise [Eio.Io] with [Error.E error] on failure.
44 Context is automatically added indicating the operation being performed. *)
5566val send : Client.t -> Message.t -> Message_response.t
···1616(** Delete a message.
1717 @raise Eio.Io on failure *)
18181919-val get : Client.t -> message_id:int -> Zulip_types.json
1919+val get : Client.t -> message_id:int -> Jsont.json
2020(** Get a single message by ID.
2121 @raise Eio.Io on failure *)
2222···2727 ?num_after:int ->
2828 ?narrow:string list ->
2929 unit ->
3030- Zulip_types.json
3030+ Jsont.json
3131(** Get multiple messages with optional filtering.
3232 @raise Eio.Io on failure *)
3333
+9-12
lib/zulip/users.ml
···1010 match Encode.from_json response_codec json with
1111 | Ok users -> users
1212 | Error msg ->
1313- let err = Zulip_types.create_error ~code:(Other "json_parse") ~msg () in
1414- raise (Eio.Exn.add_context (Zulip_types.err err) "parsing users list")
1313+ Error.raise_with_context
1414+ (Error.make ~code:(Other "json_parse") ~message:msg ())
1515+ "parsing users list"
15161617let get client ~email =
1718 (* Define a codec for the response that wraps the user in a "user" field *)
···3132 (match Encode.from_json User.jsont json with
3233 | Ok user -> user
3334 | Error msg ->
3434- let err =
3535- Zulip_types.create_error ~code:(Other "json_parse") ~msg ()
3636- in
3737- raise (Eio.Exn.add_context (Zulip_types.err err) "parsing user %s" email))
3535+ Error.raise_with_context
3636+ (Error.make ~code:(Other "json_parse") ~message:msg ())
3737+ "parsing user %s" email)
38383939let get_by_id client ~user_id =
4040 (* Define a codec for the response that wraps the user in a "user" field *)
···5656 (match Encode.from_json User.jsont json with
5757 | Ok user -> user
5858 | Error msg ->
5959- let err =
6060- Zulip_types.create_error ~code:(Other "json_parse") ~msg ()
6161- in
6262- raise
6363- (Eio.Exn.add_context (Zulip_types.err err) "parsing user id %d"
6464- user_id))
5959+ Error.raise_with_context
6060+ (Error.make ~code:(Other "json_parse") ~message:msg ())
6161+ "parsing user id %d" user_id)
65626663(* Request type for create_user *)
6764module Create_user_request = struct
+1-1
lib/zulip/users.mli
···11(** User operations for the Zulip API.
2233- All functions raise [Eio.Io] with [Zulip_types.E error] on failure.
33+ All functions raise [Eio.Io] with [Error.E error] on failure.
44 Context is automatically added indicating the operation being performed. *)
5566val list : Client.t -> User.t list
···5566 {1 Error Handling}
7788- Errors are raised as [Eio.Io] exceptions with [Zulip_types.E error],
88+ Errors are raised as [Eio.Io] exceptions with [Error.E error],
99 following the Eio error pattern (like [Eio.Net.E] and [Eio.Fs.E]).
1010 This provides context-aware error handling with automatic context
1111 accumulation as errors propagate up the call stack.
···1515 try
1616 Client.request client ~method_:`GET ~path:"/api/v1/users" ()
1717 with
1818- | Eio.Io (Zulip_types.E { code = Invalid_api_key; message; _ }, _) ->
1818+ | Eio.Io (Error.E { code = Invalid_api_key; message; _ }, _) ->
1919 (* Handle authentication error *)
2020 Log.err (fun m -> m "Auth failed: %s" message)
2121 | Eio.Io _ as ex ->
···2828(** {1 Core Types} *)
29293030(** JSON type used throughout the API *)
3131-type json = Zulip_types.json
3232-3333-(** {2 Protocol Error Types}
3131+type json = Jsont.json
34323535- Protocol errors are raised as [Eio.Io] exceptions. See {!Zulip_types}
3636- for the full error type definitions and helper functions. *)
3333+(** {1 Submodules} *)
37343838-(** Error codes returned by Zulip API *)
3939-type error_code = Zulip_types.error_code =
4040- | Invalid_api_key
4141- | Request_variable_missing
4242- | Bad_request
4343- | User_deactivated
4444- | Realm_deactivated
4545- | Rate_limit_hit
4646- | Other of string
4747-4848-(** Zulip protocol error type *)
4949-type error = Zulip_types.error = {
5050- code : error_code;
5151- message : string;
5252- extra : (string * json) list;
5353-}
5454-5555-(** Extend [Eio.Exn.err] with Zulip protocol errors *)
5656-type Eio.Exn.err += E of error
5757-5858-(** {3 Error Functions} *)
5959-6060-val err : error -> exn
6161-(** [err e] creates an [Eio.Io] exception for error [e] *)
6262-6363-val create_error :
6464- code:error_code ->
6565- msg:string ->
6666- ?extra:(string * json) list ->
6767- unit ->
6868- error
6969-7070-val error_code : error -> error_code
7171-val error_message : error -> string
7272-val error_extra : error -> (string * json) list
7373-val pp_error_code : Format.formatter -> error_code -> unit
7474-val pp_error : Format.formatter -> error -> unit
7575-val error_of_json : json -> error option
7676-7777-(** {1 Submodules} *)
3535+(** API error handling *)
3636+module Error = Error
78377938(** Authentication management *)
8039module Auth = Auth
-105
lib/zulip/zulip_types.ml
···11-(** Core types and errors for Zulip API *)
22-33-type json = Jsont.json
44-55-type error_code =
66- | Invalid_api_key
77- | Request_variable_missing
88- | Bad_request
99- | User_deactivated
1010- | Realm_deactivated
1111- | Rate_limit_hit
1212- | Other of string
1313-1414-type error = {
1515- code : error_code;
1616- message : string;
1717- extra : (string * json) list;
1818-}
1919-2020-(** Extend Eio.Exn.err with Zulip protocol errors *)
2121-type Eio.Exn.err += E of error
2222-2323-let err e = Eio.Exn.create (E e)
2424-2525-(** Pretty printing for error codes *)
2626-let pp_error_code fmt = function
2727- | Invalid_api_key -> Format.fprintf fmt "Invalid_api_key"
2828- | Request_variable_missing -> Format.fprintf fmt "Request_variable_missing"
2929- | Bad_request -> Format.fprintf fmt "Bad_request"
3030- | User_deactivated -> Format.fprintf fmt "User_deactivated"
3131- | Realm_deactivated -> Format.fprintf fmt "Realm_deactivated"
3232- | Rate_limit_hit -> Format.fprintf fmt "Rate_limit_hit"
3333- | Other s -> Format.fprintf fmt "Other(%s)" s
3434-3535-let pp_error fmt t =
3636- Format.fprintf fmt "%a: %s" pp_error_code t.code t.message
3737-3838-(** Register the pretty printer with Eio.Exn *)
3939-let () =
4040- Eio.Exn.register_pp (fun f -> function
4141- | E e ->
4242- Format.fprintf f "Zulip %a" pp_error e;
4343- true
4444- | _ -> false
4545- )
4646-4747-(** Internal: convert error_code to API string representation *)
4848-let error_code_to_api_string = function
4949- | Invalid_api_key -> "INVALID_API_KEY"
5050- | Request_variable_missing -> "REQUEST_VARIABLE_MISSING"
5151- | Bad_request -> "BAD_REQUEST"
5252- | User_deactivated -> "USER_DEACTIVATED"
5353- | Realm_deactivated -> "REALM_DEACTIVATED"
5454- | Rate_limit_hit -> "RATE_LIMIT_HIT"
5555- | Other s -> s
5656-5757-(** Internal: parse error_code from API string representation *)
5858-let error_code_of_api_string s =
5959- match s with
6060- | "INVALID_API_KEY" -> Invalid_api_key
6161- | "REQUEST_VARIABLE_MISSING" -> Request_variable_missing
6262- | "BAD_REQUEST" -> Bad_request
6363- | "USER_DEACTIVATED" -> User_deactivated
6464- | "REALM_DEACTIVATED" -> Realm_deactivated
6565- | "RATE_LIMIT_HIT" -> Rate_limit_hit
6666- | s -> Other s
6767-6868-let create_error ~code ~msg ?(extra = []) () = { code; message = msg; extra }
6969-let error_code t = t.code
7070-let error_message t = t.message
7171-let error_extra t = t.extra
7272-7373-(** Jsont codec for error_code *)
7474-let error_code_jsont =
7575- let of_string s = Ok (error_code_of_api_string s) in
7676- Jsont.of_of_string ~kind:"ErrorCode" of_string ~enc:error_code_to_api_string
7777-7878-(** Jsont codec for error *)
7979-let error_jsont =
8080- let kind = "ZulipError" in
8181- let make code msg =
8282- { code = error_code_of_api_string code; message = msg; extra = [] }
8383- in
8484- let code t = error_code_to_api_string t.code in
8585- let msg t = t.message in
8686- Jsont.Object.(
8787- map ~kind make
8888- |> mem "code" Jsont.string ~enc:code
8989- |> mem "msg" Jsont.string ~enc:msg
9090- |> finish
9191- )
9292-9393-let error_of_json json =
9494- match Encode.from_json error_jsont json with
9595- | Ok err ->
9696- (* Extract extra fields by getting all fields except code, msg, result *)
9797- (match json with
9898- | Jsont.Object (fields, _) ->
9999- let assoc = List.map (fun ((k, _), v) -> (k, v)) fields in
100100- let extra =
101101- List.filter (fun (k, _) -> k <> "code" && k <> "msg" && k <> "result") assoc
102102- in
103103- Some { err with extra }
104104- | _ -> Some err)
105105- | Error _ -> None
-73
lib/zulip/zulip_types.mli
···11-(** Core types and errors for Zulip API.
22-33- This module defines the protocol-level error types for the Zulip API,
44- following the Eio.Io error pattern for context-aware error handling. *)
55-66-(** JSON type used throughout the API *)
77-type json = Jsont.json
88-99-(** {1 Protocol Error Codes}
1010-1111- These error codes correspond to the error codes returned by the Zulip API
1212- in the "code" field of error responses. *)
1313-1414-type error_code =
1515- | Invalid_api_key (** Authentication failure - invalid API key *)
1616- | Request_variable_missing (** Required parameter is missing *)
1717- | Bad_request (** Malformed request *)
1818- | User_deactivated (** User account has been deactivated *)
1919- | Realm_deactivated (** Organization (realm) has been deactivated *)
2020- | Rate_limit_hit (** API rate limit exceeded *)
2121- | Other of string (** Other/unknown error code *)
2222-2323-(** {1 Zulip Protocol Errors}
2424-2525- Protocol errors are raised as [Eio.Io] exceptions with context,
2626- following the same pattern as [Eio.Net.E] and [Eio.Fs.E]. *)
2727-2828-(** The protocol-level error type, containing the error code, message,
2929- and any extra fields returned by the API. *)
3030-type error = {
3131- code : error_code; (** The error code from the API *)
3232- message : string; (** Human-readable error message *)
3333- extra : (string * json) list; (** Additional fields from the error response *)
3434-}
3535-3636-(** Extend [Eio.Exn.err] with Zulip protocol errors. *)
3737-type Eio.Exn.err += E of error
3838-3939-(** [err e] creates an [Eio.Io] exception for error [e].
4040- Equivalent to [Eio.Exn.create (E e)]. *)
4141-val err : error -> exn
4242-4343-(** {2 Error Construction} *)
4444-4545-val create_error :
4646- code:error_code ->
4747- msg:string ->
4848- ?extra:(string * json) list ->
4949- unit ->
5050- error
5151-(** [create_error ~code ~msg ?extra ()] creates an error value. *)
5252-5353-(** {2 Error Accessors} *)
5454-5555-val error_code : error -> error_code
5656-val error_message : error -> string
5757-val error_extra : error -> (string * json) list
5858-5959-(** {2 Pretty Printing} *)
6060-6161-val pp_error_code : Format.formatter -> error_code -> unit
6262-val pp_error : Format.formatter -> error -> unit
6363-6464-(** {1 Jsont Codecs}
6565-6666- These codecs are used for parsing API error responses from JSON. *)
6767-6868-val error_code_jsont : error_code Jsont.t
6969-val error_jsont : error Jsont.t
7070-7171-(** [error_of_json json] attempts to parse a Zulip API error response.
7272- Returns [None] if the JSON does not represent an error. *)
7373-val error_of_json : json -> error option