Requesting repo: gsmlg-dev/http_fetch on branch main
HTTP.fetch/2 with method: "OPTIONS" resolves to {:error, :invalid_request} regardless of the URL, headers, or body.
Root cause (observed)
This is an upstream :httpc limitation rather than a bug in http_fetch itself. :httpc.request/4 is documented to reject OPTIONS with {:error, :invalid_request} because the :httpc profile (an old HTTP/1.0 client) does not implement OPTIONS. The error is returned synchronously by :httpc.request/4 before any network I/O happens, regardless of the request target.
The e2e/methods_test.exs OPTIONS test exercises the local Go test server's /options route (which returns 204 No Content with an Allow header listing the supported methods) and currently fails because HTTP.fetch(method: "OPTIONS") short-circuits to {:error, :invalid_request} before any request is sent.
Reproduction
resp =
"http://127.0.0.1:<port>/options"
|> HTTP.fetch(method: "OPTIONS")
|> HTTP.Promise.await()
# Expected: %HTTP.Response{status: 204, headers: %{"allow" => "GET, POST, ..."}}
# Actual: {:error, :invalid_request}
Impact
HTTP.fetch/2 is documented as a browser-like fetch API; OPTIONS (used for CORS preflight) is a first-class HTTP verb that the library does not actually support.
- The current behaviour is silent — the user sees an opaque
:invalid_request atom with no hint that :httpc is the source.
Suggested direction (not requesting a specific implementation)
- For OPTIONS specifically, build the request manually using
:gen_tcp/:ssl (or a small dedicated module) and parse the response, since :httpc will not be updated to support it.
- If switching transports is out of scope, detect the OPTIONS method in
HTTP.fetch/2 and raise a clear ArgumentError with a message explaining that :httpc does not support OPTIONS, instead of letting the opaque :invalid_request propagate.
Environment
- Elixir 1.18+
- Erlang/OTP 28
Severity
nice-to-have (OPTIONS/CORS preflight is typically handled by the browser; server-to-server OPTIONS is uncommon, and the failure mode is obvious enough that users can work around it)
Requesting repo: gsmlg-dev/http_fetch on branch
mainHTTP.fetch/2withmethod: "OPTIONS"resolves to{:error, :invalid_request}regardless of the URL, headers, or body.Root cause (observed)
This is an upstream
:httpclimitation rather than a bug inhttp_fetchitself.:httpc.request/4is documented to rejectOPTIONSwith{:error, :invalid_request}because the:httpcprofile (an old HTTP/1.0 client) does not implement OPTIONS. The error is returned synchronously by:httpc.request/4before any network I/O happens, regardless of the request target.The
e2e/methods_test.exsOPTIONS test exercises the local Go test server's/optionsroute (which returns204 No Contentwith anAllowheader listing the supported methods) and currently fails becauseHTTP.fetch(method: "OPTIONS")short-circuits to{:error, :invalid_request}before any request is sent.Reproduction
Impact
HTTP.fetch/2is documented as a browser-like fetch API; OPTIONS (used for CORS preflight) is a first-class HTTP verb that the library does not actually support.:invalid_requestatom with no hint that:httpcis the source.Suggested direction (not requesting a specific implementation)
:gen_tcp/:ssl(or a small dedicated module) and parse the response, since:httpcwill not be updated to support it.HTTP.fetch/2and raise a clearArgumentErrorwith a message explaining that:httpcdoes not support OPTIONS, instead of letting the opaque:invalid_requestpropagate.Environment
Severity
nice-to-have (OPTIONS/CORS preflight is typically handled by the browser; server-to-server OPTIONS is uncommon, and the failure mode is obvious enough that users can work around it)