Errors, retries and telemetry

Copy Markdown View Source

Errors

Every function except the paging streams returns {:ok, result} or {:error, %Jibu.Error{}}. Jibu.Models.stream/2, Jibu.Files.stream/2 and Jibu.Batches.stream/2 page through lists lazily. They raise Jibu.Error, because a lazy stream cannot return an error tuple. They page forward only and reject :before_id. Jibu.stream/3 streams a response and returns a tuple like every other function.

FieldMeaning
typeThe API's error.type, or one of Jibu's own types
messageHuman-readable description
statusHTTP status, nil without a response
request_idThe API's request id, for support requests
retry_afterDelay suggested by the API, in milliseconds
reasonThe underlying exception of a transport error
bodyThe decoded response body
messagesSet by Jibu.Tools.run/3: the conversation of the failed request or at the iteration cap

Jibu's own types:

TypeCause
"transport_error"No HTTP response, for example a refused connection
"refusal"Jibu.Response.json/1 on a refused request
"max_tokens"Jibu.Response.json/1 on output cut off at max_tokens
"context_window_exceeded"Jibu.Response.json/1 on output cut off at the context window
"tool_use", "pause_turn"ask/3, extract/4 or Jibu.Response.json/1 on a turn that waits for tools
"invalid_json"Jibu.Response.json/1 on text that is not JSON, or stream event data that is not JSON
"invalid_stream"Jibu.stream/3 got a success response that is not an event stream
"max_iterations"Jibu.Tools.run/3 reached its request cap

An error status without a JSON error body gets the type "http_error".

Retries

Jibu retries these responses, for every HTTP method:

  • 408, 409 and 429
  • every 5xx status, including 529, the API's overloaded_error

It also retries timeouts, refused connections and closed connections.

A retry-after header sets the delay. Without one, Req backs off exponentially with jitter. An x-should-retry header from the API overrides the status rule. Req makes at most 3 retries by default. Set :max_retries to change that.

Jibu.new(max_retries: 5)
Jibu.new(retry: false)

Telemetry

Every request runs in a :telemetry.span/3 named [:jibu, :request].

EventMeasurementsMetadata
[:jibu, :request, :start]system_time, monotonic_timemethod, path, model
[:jibu, :request, :stop]duration, monotonic_timemethod, path, model, status, usage
[:jibu, :request, :exception]duration, monotonic_timemethod, path, model, kind, reason, stacktrace

model is nil for endpoints without a model in the body. status is nil for transport errors. usage is the API's usage map, or nil. Retries happen inside one span, so duration includes them.

require Logger

:telemetry.attach("jibu-usage", [:jibu, :request, :stop], fn _event, %{duration: d}, meta, _ ->
  ms = System.convert_time_unit(d, :native, :millisecond)
  Logger.info("#{meta.model} #{meta.status} #{ms}ms #{inspect(meta.usage)}")
end, nil)