Skip to content

Implement LibCURL-based streaming with improved error handling#16

Open
Sixzero wants to merge 11 commits intosvilupp:mainfrom
Sixzero:feature/libcurl-iobuffer-support
Open

Implement LibCURL-based streaming with improved error handling#16
Sixzero wants to merge 11 commits intosvilupp:mainfrom
Sixzero:feature/libcurl-iobuffer-support

Conversation

@Sixzero
Copy link
Copy Markdown
Contributor

@Sixzero Sixzero commented Jun 21, 2025

Key advantages

  • Add libcurl_streamed_request! with cleaner stack traces
  • Showing error messages on API response failures

Notes:

  • Examples in long_context_test.jl and error_handling_test.jl

I think there was an error in the throw_on_error, because Exception didn't have Exception(::String) initialization.

P.S: Output comparisons will be posted later

- Add libcurl_streamed_request! with cleaner stack traces
- Better error messages on API response failures
- Examples in long_context_test.jl and error_handling_test.jl
- Output comparisons will be posted in PR
@Sixzero
Copy link
Copy Markdown
Contributor Author

Sixzero commented Jun 21, 2025

The goal is to eventually switch away from the HTTP.jl solution, since they cannot reduce the size of the stacktrace, according to slack conversations, also I posted this for them: JuliaWeb/HTTP.jl#1216
But no reaction sadly. :/

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Jun 21, 2025

Codecov Report

Attention: Patch coverage is 1.26582% with 78 lines in your changes missing coverage. Please review.

Project coverage is 67.52%. Comparing base (9bc62f3) to head (5d21dab).

Files with missing lines Patch % Lines
src/shared_methods_libcurl.jl 0.00% 78 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main      #16       +/-   ##
===========================================
- Coverage   86.81%   67.52%   -19.30%     
===========================================
  Files           6        7        +1     
  Lines         273      351       +78     
===========================================
  Hits          237      237               
- Misses         36      114       +78     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Sixzero
Copy link
Copy Markdown
Contributor Author

Sixzero commented Jun 21, 2025

Runing the long_context_test.jl:
HTTP.jl error:

1. Testing HTTP.jl streaming...
ERROR: HTTP.RequestError:
HTTP.Request:
HTTP.Messages.Request:
"""
POST /v1/chat/completions HTTP/1.1
Content-Type: application/json
Authorization: ******
Host: api.openai.com
Accept: */*
User-Agent: HTTP.jl/1.11.5
Accept-Encoding: gzip
Transfer-Encoding: chunked

[Message Body was streamed]"""Underlying error:
AssertionError: Content-Type header should include the type text/event-stream.
Received type: application/json
Status code: 400
Response headers:
 - Date: Sat, 21 Jun 2025 23:22:18 GMT
 - Content-Type: application/json
 - Content-Length: 284
 - Connection: keep-alive
 - access-control-expose-headers: X-Request-ID
 - openai-organization: user-nouruvhwbaxyqe8v2n5h0ojx
 - openai-processing-ms: 1529
 - openai-version: 2020-10-01
 - x-envoy-upstream-service-time: 1536
 - x-ratelimit-limit-requests: 5000
 - x-ratelimit-limit-tokens: 4000000
 - x-ratelimit-remaining-requests: 4999
 - x-ratelimit-remaining-tokens: 3327768
 - x-ratelimit-reset-requests: 12ms
 - x-ratelimit-reset-tokens: 10.083s
 - x-request-id: req_ff0f26743d6b1dc6012e727f1ea4aeaf
 - strict-transport-security: max-age=31536000; includeSubDomains; preload
 - cf-cache-status: DYNAMIC
 - Set-Cookie: __cf_bm=X5o.ntmTnbL28dCc__7X23QYHAi9IMa.O8ly1bt_qy4-1750548138-1.0.1.1-xzFMMcIqunovqPLHjs_YBgN622lgWJD9y.M_a2XuM4a0NsrH4cz66XvoIk8nEyggSZ31GLrbPFMlz1xqg4aaED5CTuGKsjuW24s9bILKXrI; path=/; expires=Sat, 21-Jun-25 23:52:18 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
 - X-Content-Type-Options: nosniff
 - Set-Cookie: _cfuvid=7dZEHlu0aJYSnLfSg9rpuH0TF65dFfWmGphHq4S7Wt4-1750548138771-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
 - Server: cloudflare
 - CF-RAY: 95373f39bb365ba3-VIE
 - alt-svc: h3=":443"; ma=86400
Response body: 
Please check the model you are using and that you set `stream=true`.

Stacktrace:
  [1] (::HTTP.ConnectionRequest.var"#connections#4"{…})(req::HTTP.Messages.Request; proxy::Nothing, socket_type::Type, socket_type_tls::Nothing, readtimeout::Int64, connect_timeout::Int64, logerrors::Bool, logtag::Nothing, closeimmediately::Bool, kw::@Kwargs{…})
    @ HTTP.ConnectionRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:143
  [2] connections
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:60 [inlined]
  [3] (::Base.var"#106#108"{…})(args::HTTP.Messages.Request; kwargs::@Kwargs{…})
    @ Base ./error.jl:300
  [4] (::HTTP.RetryRequest.var"#manageretries#3"{…})(req::HTTP.Messages.Request; retry::Bool, retries::Int64, retry_delays::ExponentialBackOff, retry_check::Function, retry_non_idempotent::Bool, kw::@Kwargs{…})
    @ HTTP.RetryRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:75
  [5] manageretries
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:30 [inlined]
  [6] (::HTTP.CookieRequest.var"#managecookies#4"{…})(req::HTTP.Messages.Request; cookies::Bool, cookiejar::HTTP.Cookies.CookieJar, kw::@Kwargs{…})
    @ HTTP.CookieRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/CookieRequest.jl:42
  [7] (::HTTP.HeadersRequest.var"#defaultheaders#2"{…})(req::HTTP.Messages.Request; iofunction::Function, decompress::Nothing, basicauth::Bool, detect_content_type::Bool, canonicalize_headers::Bool, kw::@Kwargs{…})
    @ HTTP.HeadersRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:71
  [8] defaultheaders
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:14 [inlined]
  [9] (::HTTP.RedirectRequest.var"#redirects#3"{…})(req::HTTP.Messages.Request; redirect::Bool, redirect_limit::Int64, redirect_method::Nothing, forwardheaders::Bool, response_stream::Nothing, kw::@Kwargs{…})
    @ HTTP.RedirectRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:25
 [10] redirects
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:14 [inlined]
 [11] (::HTTP.MessageRequest.var"#makerequest#3"{…})(method::String, url::URIs.URI, headers::Vector{…}, body::Nothing; copyheaders::Bool, response_stream::Nothing, http_version::HTTP.Strings.HTTPVersion, verbose::Int64, kw::@Kwargs{…})
    @ HTTP.MessageRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:35
 [12] makerequest
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:24 [inlined]
 [13] request(stack::HTTP.MessageRequest.var"#makerequest#3"{…}, method::String, url::String, h::Vector{…}, b::Nothing, q::Nothing; headers::Vector{…}, body::Nothing, query::Nothing, kw::@Kwargs{…})
    @ HTTP ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:457
 [14] #request#20
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:315 [inlined]
 [15] request
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:313 [inlined]
 [16] open
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:579 [inlined]
 [17] streamed_request!(cb::StreamCallback{…}, url::String, headers::Vector{…}, input::IOBuffer; kwargs::@Kwargs{})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:197
 [18] streamed_request!(cb::StreamCallback{Base.TTY}, url::String, headers::Vector{Pair{String, String}}, input::IOBuffer)
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:195
 [19] macro expansion
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/long_context_test.jl:28 [inlined]
 [20] macro expansion
    @ ./timing.jl:581 [inlined]
 [21] top-level scope
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/long_context_test.jl:27

caused by: AssertionError: Content-Type header should include the type text/event-stream.
Received type: application/json
Status code: 400
Response headers:
 - Date: Sat, 21 Jun 2025 23:22:18 GMT
 - Content-Type: application/json
 - Content-Length: 284
 - Connection: keep-alive
 - access-control-expose-headers: X-Request-ID
 - openai-organization: user-nouruvhwbaxyqe8v2n5h0ojx
 - openai-processing-ms: 1529
 - openai-version: 2020-10-01
 - x-envoy-upstream-service-time: 1536
 - x-ratelimit-limit-requests: 5000
 - x-ratelimit-limit-tokens: 4000000
 - x-ratelimit-remaining-requests: 4999
 - x-ratelimit-remaining-tokens: 3327768
 - x-ratelimit-reset-requests: 12ms
 - x-ratelimit-reset-tokens: 10.083s
 - x-request-id: req_ff0f26743d6b1dc6012e727f1ea4aeaf
 - strict-transport-security: max-age=31536000; includeSubDomains; preload
 - cf-cache-status: DYNAMIC
 - Set-Cookie: __cf_bm=X5o.ntmTnbL28dCc__7X23QYHAi9IMa.O8ly1bt_qy4-1750548138-1.0.1.1-xzFMMcIqunovqPLHjs_YBgN622lgWJD9y.M_a2XuM4a0NsrH4cz66XvoIk8nEyggSZ31GLrbPFMlz1xqg4aaED5CTuGKsjuW24s9bILKXrI; path=/; expires=Sat, 21-Jun-25 23:52:18 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
 - X-Content-Type-Options: nosniff
 - Set-Cookie: _cfuvid=7dZEHlu0aJYSnLfSg9rpuH0TF65dFfWmGphHq4S7Wt4-1750548138771-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None
 - Server: cloudflare
 - CF-RAY: 95373f39bb365ba3-VIE
 - alt-svc: h3=":443"; ma=86400
Response body: 
Please check the model you are using and that you set `stream=true`.

Stacktrace:
  [1] (::StreamCallbacks.var"#65#69"{…})(stream::HTTP.Streams.Stream{…})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:213
  [2] macro expansion
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/StreamRequest.jl:65 [inlined]
  [3] macro expansion
    @ ./task.jl:498 [inlined]
  [4] streamlayer(stream::HTTP.Streams.Stream{…}; iofunction::StreamCallbacks.var"#65#69"{…}, decompress::Nothing, logerrors::Bool, logtag::Nothing, timedout::Nothing, kw::@Kwargs{…})
    @ HTTP.StreamRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/StreamRequest.jl:35
  [5] streamlayer
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/StreamRequest.jl:21 [inlined]
  [6] (::HTTP.ExceptionRequest.var"#exceptions#2"{…})(stream::HTTP.Streams.Stream{…}; status_exception::Bool, timedout::Nothing, logerrors::Bool, logtag::Nothing, kw::@Kwargs{…})
    @ HTTP.ExceptionRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ExceptionRequest.jl:14
  [7] exceptions
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ExceptionRequest.jl:13 [inlined]
  [8] (::HTTP.TimeoutRequest.var"#timeouts#3"{…})(stream::HTTP.Streams.Stream{…}; readtimeout::Int64, logerrors::Bool, logtag::Nothing, kw::@Kwargs{…})
    @ HTTP.TimeoutRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/TimeoutRequest.jl:18
  [9] (::HTTP.ConnectionRequest.var"#connections#4"{…})(req::HTTP.Messages.Request; proxy::Nothing, socket_type::Type, socket_type_tls::Nothing, readtimeout::Int64, connect_timeout::Int64, logerrors::Bool, logtag::Nothing, closeimmediately::Bool, kw::@Kwargs{…})
    @ HTTP.ConnectionRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:122
 [10] connections
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:60 [inlined]
 [11] (::Base.var"#106#108"{…})(args::HTTP.Messages.Request; kwargs::@Kwargs{…})
    @ Base ./error.jl:300
 [12] (::HTTP.RetryRequest.var"#manageretries#3"{…})(req::HTTP.Messages.Request; retry::Bool, retries::Int64, retry_delays::ExponentialBackOff, retry_check::Function, retry_non_idempotent::Bool, kw::@Kwargs{…})
    @ HTTP.RetryRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:75
 [13] manageretries
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:30 [inlined]
 [14] (::HTTP.CookieRequest.var"#managecookies#4"{…})(req::HTTP.Messages.Request; cookies::Bool, cookiejar::HTTP.Cookies.CookieJar, kw::@Kwargs{…})
    @ HTTP.CookieRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/CookieRequest.jl:42
 [15] (::HTTP.HeadersRequest.var"#defaultheaders#2"{…})(req::HTTP.Messages.Request; iofunction::Function, decompress::Nothing, basicauth::Bool, detect_content_type::Bool, canonicalize_headers::Bool, kw::@Kwargs{…})
    @ HTTP.HeadersRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:71
 [16] defaultheaders
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:14 [inlined]
 [17] (::HTTP.RedirectRequest.var"#redirects#3"{…})(req::HTTP.Messages.Request; redirect::Bool, redirect_limit::Int64, redirect_method::Nothing, forwardheaders::Bool, response_stream::Nothing, kw::@Kwargs{…})
    @ HTTP.RedirectRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:25
 [18] redirects
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:14 [inlined]
 [19] (::HTTP.MessageRequest.var"#makerequest#3"{…})(method::String, url::URIs.URI, headers::Vector{…}, body::Nothing; copyheaders::Bool, response_stream::Nothing, http_version::HTTP.Strings.HTTPVersion, verbose::Int64, kw::@Kwargs{…})
    @ HTTP.MessageRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:35
 [20] makerequest
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:24 [inlined]
 [21] request(stack::HTTP.MessageRequest.var"#makerequest#3"{…}, method::String, url::String, h::Vector{…}, b::Nothing, q::Nothing; headers::Vector{…}, body::Nothing, query::Nothing, kw::@Kwargs{…})
    @ HTTP ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:457
 [22] #request#20
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:315 [inlined]
 [23] request
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:313 [inlined]
 [24] open
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:579 [inlined]
 [25] streamed_request!(cb::StreamCallback{…}, url::String, headers::Vector{…}, input::IOBuffer; kwargs::@Kwargs{})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:197
 [26] streamed_request!(cb::StreamCallback{Base.TTY}, url::String, headers::Vector{Pair{String, String}}, input::IOBuffer)
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:195
 [27] macro expansion
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/long_context_test.jl:28 [inlined]
 [28] macro expansion
    @ ./timing.jl:581 [inlined]
 [29] top-level scope
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/long_context_test.jl:27
Some type information was truncated. Use `show(err)` to see complete types.

New LibCURL.jl error:

2. Testing LibCURL streaming...
ERROR: Error detected in the streaming response: Message: This model's maximum context length is 128000 tokens. However, your messages resulted in 799015 tokens. Please reduce the length of the messages., Type: invalid_request_error, Param: messages, Code: context_length_exceeded
Stacktrace:
 [1] #handle_error_message#14
   @ ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:171 [inlined]
 [2] curl_write_callback(ptr::Ptr{UInt8}, size::UInt64, nmemb::UInt64, userdata::Ptr{Nothing})
   @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods_libcurl.jl:27
 [3] curl_easy_perform
   @ ~/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/share/julia/stdlib/v1.11/LibCURL/src/lC_curl_h.jl:162 [inlined]
 [4] libcurl_streamed_request!(cb::StreamCallback{…}, url::String, headers::Vector{…}, body::String; kwargs::@Kwargs{})
   @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods_libcurl.jl:120
 [5] libcurl_streamed_request!(cb::StreamCallback{Base.TTY}, url::String, headers::Vector{Pair{…}}, body::String)
   @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods_libcurl.jl:74
 [6] macro expansion
   @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/long_context_test.jl:37 [inlined]
 [7] macro expansion
   @ ./timing.jl:581 [inlined]
 [8] top-level scope
   @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/long_context_test.jl:36
Some type information was truncated. Use `show(err)` to see complete types.

I think we can clearly see what is my problem with HTTP.jl here.

I 'll post the other test's output.

@Sixzero
Copy link
Copy Markdown
Contributor Author

Sixzero commented Jun 21, 2025

Running julia examples/error_handling_test.jl
HTTP.jl:

=== Testing Error Handling ===

1. Testing HTTP.jl error handling...
ERROR: HTTP.RequestError:
HTTP.Request:
HTTP.Messages.Request:
"""
POST /v1/chat/completions HTTP/1.1
Content-Type: application/json
Authorization: ******
Host: api.openai.com
Accept: */*
User-Agent: HTTP.jl/1.11.5
Accept-Encoding: gzip
Transfer-Encoding: chunked

[Message Body was streamed]"""Underlying error:
Custom IO error: Found forbidden number '5' in: 5
Stacktrace:
  [1] (::HTTP.ConnectionRequest.var"#connections#4"{…})(req::HTTP.Messages.Request; proxy::Nothing, socket_type::Type, socket_type_tls::Nothing, readtimeout::Int64, connect_timeout::Int64, logerrors::Bool, logtag::Nothing, closeimmediately::Bool, kw::@Kwargs{…})
    @ HTTP.ConnectionRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:143
  [2] connections
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:60 [inlined]
  [3] (::Base.var"#106#108"{…})(args::HTTP.Messages.Request; kwargs::@Kwargs{…})
    @ Base ./error.jl:300
  [4] (::HTTP.RetryRequest.var"#manageretries#3"{…})(req::HTTP.Messages.Request; retry::Bool, retries::Int64, retry_delays::ExponentialBackOff, retry_check::Function, retry_non_idempotent::Bool, kw::@Kwargs{…})
    @ HTTP.RetryRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:75
  [5] manageretries
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:30 [inlined]
  [6] (::HTTP.CookieRequest.var"#managecookies#4"{…})(req::HTTP.Messages.Request; cookies::Bool, cookiejar::HTTP.Cookies.CookieJar, kw::@Kwargs{…})
    @ HTTP.CookieRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/CookieRequest.jl:42
  [7] (::HTTP.HeadersRequest.var"#defaultheaders#2"{…})(req::HTTP.Messages.Request; iofunction::Function, decompress::Nothing, basicauth::Bool, detect_content_type::Bool, canonicalize_headers::Bool, kw::@Kwargs{…})
    @ HTTP.HeadersRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:71
  [8] defaultheaders
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:14 [inlined]
  [9] (::HTTP.RedirectRequest.var"#redirects#3"{…})(req::HTTP.Messages.Request; redirect::Bool, redirect_limit::Int64, redirect_method::Nothing, forwardheaders::Bool, response_stream::Nothing, kw::@Kwargs{…})
    @ HTTP.RedirectRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:25
 [10] redirects
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:14 [inlined]
 [11] (::HTTP.MessageRequest.var"#makerequest#3"{…})(method::String, url::URIs.URI, headers::Vector{…}, body::Nothing; copyheaders::Bool, response_stream::Nothing, http_version::HTTP.Strings.HTTPVersion, verbose::Int64, kw::@Kwargs{…})
    @ HTTP.MessageRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:35
 [12] makerequest
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:24 [inlined]
 [13] request(stack::HTTP.MessageRequest.var"#makerequest#3"{…}, method::String, url::String, h::Vector{…}, b::Nothing, q::Nothing; headers::Vector{…}, body::Nothing, query::Nothing, kw::@Kwargs{…})
    @ HTTP ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:457
 [14] #request#20
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:315 [inlined]
 [15] request
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:313 [inlined]
 [16] open
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:579 [inlined]
 [17] streamed_request!(cb::StreamCallback{…}, url::String, headers::Vector{…}, input::IOBuffer; kwargs::@Kwargs{})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:197
 [18] streamed_request!(cb::StreamCallback{ErrorOnFiveIO}, url::String, headers::Vector{Pair{…}}, input::IOBuffer)
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:195
 [19] top-level scope
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/error_handling_test.jl:36

caused by: Custom IO error: Found forbidden number '5' in: 5
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:35
  [2] #print_content#54
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/error_handling_test.jl:22 [inlined]
  [3] callback(cb::StreamCallback{ErrorOnFiveIO}, chunk::StreamChunk{String, JSON3.Object{…}}; kwargs::@Kwargs{verbose::Bool})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:143
  [4] callback
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:140 [inlined]
  [5] (::StreamCallbacks.var"#65#69"{…})(stream::HTTP.Streams.Stream{…})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:240
  [6] macro expansion
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/StreamRequest.jl:65 [inlined]
  [7] macro expansion
    @ ./task.jl:498 [inlined]
  [8] streamlayer(stream::HTTP.Streams.Stream{…}; iofunction::StreamCallbacks.var"#65#69"{…}, decompress::Nothing, logerrors::Bool, logtag::Nothing, timedout::Nothing, kw::@Kwargs{…})
    @ HTTP.StreamRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/StreamRequest.jl:35
  [9] streamlayer
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/StreamRequest.jl:21 [inlined]
 [10] (::HTTP.ExceptionRequest.var"#exceptions#2"{…})(stream::HTTP.Streams.Stream{…}; status_exception::Bool, timedout::Nothing, logerrors::Bool, logtag::Nothing, kw::@Kwargs{…})
    @ HTTP.ExceptionRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ExceptionRequest.jl:14
 [11] exceptions
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ExceptionRequest.jl:13 [inlined]
 [12] (::HTTP.TimeoutRequest.var"#timeouts#3"{…})(stream::HTTP.Streams.Stream{…}; readtimeout::Int64, logerrors::Bool, logtag::Nothing, kw::@Kwargs{…})
    @ HTTP.TimeoutRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/TimeoutRequest.jl:18
 [13] (::HTTP.ConnectionRequest.var"#connections#4"{…})(req::HTTP.Messages.Request; proxy::Nothing, socket_type::Type, socket_type_tls::Nothing, readtimeout::Int64, connect_timeout::Int64, logerrors::Bool, logtag::Nothing, closeimmediately::Bool, kw::@Kwargs{…})
    @ HTTP.ConnectionRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:122
 [14] connections
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/ConnectionRequest.jl:60 [inlined]
 [15] (::Base.var"#106#108"{…})(args::HTTP.Messages.Request; kwargs::@Kwargs{…})
    @ Base ./error.jl:300
 [16] (::HTTP.RetryRequest.var"#manageretries#3"{…})(req::HTTP.Messages.Request; retry::Bool, retries::Int64, retry_delays::ExponentialBackOff, retry_check::Function, retry_non_idempotent::Bool, kw::@Kwargs{…})
    @ HTTP.RetryRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:75
 [17] manageretries
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RetryRequest.jl:30 [inlined]
 [18] (::HTTP.CookieRequest.var"#managecookies#4"{…})(req::HTTP.Messages.Request; cookies::Bool, cookiejar::HTTP.Cookies.CookieJar, kw::@Kwargs{…})
    @ HTTP.CookieRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/CookieRequest.jl:42
 [19] (::HTTP.HeadersRequest.var"#defaultheaders#2"{…})(req::HTTP.Messages.Request; iofunction::Function, decompress::Nothing, basicauth::Bool, detect_content_type::Bool, canonicalize_headers::Bool, kw::@Kwargs{…})
    @ HTTP.HeadersRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:71
 [20] defaultheaders
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/HeadersRequest.jl:14 [inlined]
 [21] (::HTTP.RedirectRequest.var"#redirects#3"{…})(req::HTTP.Messages.Request; redirect::Bool, redirect_limit::Int64, redirect_method::Nothing, forwardheaders::Bool, response_stream::Nothing, kw::@Kwargs{…})
    @ HTTP.RedirectRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:25
 [22] redirects
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/RedirectRequest.jl:14 [inlined]
 [23] (::HTTP.MessageRequest.var"#makerequest#3"{…})(method::String, url::URIs.URI, headers::Vector{…}, body::Nothing; copyheaders::Bool, response_stream::Nothing, http_version::HTTP.Strings.HTTPVersion, verbose::Int64, kw::@Kwargs{…})
    @ HTTP.MessageRequest ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:35
 [24] makerequest
    @ ~/.julia/packages/HTTP/MIUdD/src/clientlayers/MessageRequest.jl:24 [inlined]
 [25] request(stack::HTTP.MessageRequest.var"#makerequest#3"{…}, method::String, url::String, h::Vector{…}, b::Nothing, q::Nothing; headers::Vector{…}, body::Nothing, query::Nothing, kw::@Kwargs{…})
    @ HTTP ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:457
 [26] #request#20
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:315 [inlined]
 [27] request
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:313 [inlined]
 [28] open
    @ ~/.julia/packages/HTTP/MIUdD/src/HTTP.jl:579 [inlined]
 [29] streamed_request!(cb::StreamCallback{…}, url::String, headers::Vector{…}, input::IOBuffer; kwargs::@Kwargs{})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:197
 [30] streamed_request!(cb::StreamCallback{ErrorOnFiveIO}, url::String, headers::Vector{Pair{…}}, input::IOBuffer)
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:195
 [31] top-level scope
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/error_handling_test.jl:36
Some type information was truncated. Use `show(err)` to see complete types.

LibCURL:

=== Testing Error Handling ===

2. Testing LibCURL error handling...
ERROR: Custom IO error: Found forbidden number '5' in: 5
Stacktrace:
  [1] error(s::String)
    @ Base ./error.jl:35
  [2] #print_content#55
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/error_handling_test.jl:22 [inlined]
  [3] print_content(out::ErrorOnFiveIO, text::String)
    @ Main ~/repo/jl_pkgs/StreamCallbacks.jl/examples/error_handling_test.jl:19
  [4] callback(cb::StreamCallback{ErrorOnFiveIO}, chunk::StreamChunk{String, JSON3.Object{…}}; kwargs::@Kwargs{})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:143
  [5] callback
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods.jl:140 [inlined]
  [6] curl_write_callback(ptr::Ptr{UInt8}, size::UInt64, nmemb::UInt64, userdata::Ptr{Nothing})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods_libcurl.jl:31
  [7] curl_easy_perform
    @ ~/.julia/juliaup/julia-1.11.5+0.x64.linux.gnu/share/julia/stdlib/v1.11/LibCURL/src/lC_curl_h.jl:162 [inlined]
  [8] libcurl_streamed_request!(cb::StreamCallback{…}, url::String, headers::Vector{…}, body::String; kwargs::@Kwargs{})
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods_libcurl.jl:120
  [9] libcurl_streamed_request!(cb::StreamCallback{ErrorOnFiveIO}, url::String, headers::Vector{Pair{…}}, body::String)
    @ StreamCallbacks ~/repo/jl_pkgs/StreamCallbacks.jl/src/shared_methods_libcurl.jl:74
 [10] top-level scope
    @ ~/repo/jl_pkgs/StreamCallbacks.jl/examples/error_handling_test.jl:43
Some type information was truncated. Use `show(err)` to see complete types.

Again the results speaks clearly for themselves.
120 lines vs 20 lines. Its really disturbing IMO.
BUT on the positive side, at least here the stacktrace of the HTTP.jl also has the line which really caused the failure! It is so funny in the other solution we just dropped it.

Immagine how many times there is some fault in the API call and you would need the error.

@Sixzero
Copy link
Copy Markdown
Contributor Author

Sixzero commented Jun 21, 2025

I would also propose the removal of throw_on_error from the StreamCallbacks especially not defaulting it to false. This is just a silent error! I doubt anyone ever notices we basically silently drop errors, for no good reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants