Skip to the content.

curl exit codes in C#

The curl tool reports failures with a numeric exit code. CurlDotNet maps every failure to the same number in C#: the exception it throws carries the code in CurlException.CurlErrorCode, and the exception type names the error. This page lists every code CurlDotNet produces, which exception you get, and the usual fix.

using CurlDotNet;
using CurlDotNet.Exceptions;

try
{
    var result = await Curl.ExecuteAsync("curl -f -m 10 https://httpbin.org/status/404");
}
catch (CurlException ex)
{
    Console.WriteLine($"curl: ({ex.CurlErrorCode}) {ex.Message}");   // curl: (22) ...
}

Success is not an exception

Exit code 0 is a returned CurlResult. That includes HTTP error statuses: like curl, a 404 or 500 is a successful transfer unless you add -f / --fail. Check result.StatusCode, or call result.EnsureSuccess(), which throws CurlDotNet.Core.CurlHttpException for a non-2xx status (a plain Exception with a StatusCode property, not a CurlException).

Cancellation is not an exit code either: a cancelled CancellationToken throws the standard OperationCanceledException, so catch (OperationCanceledException) works as with any other .NET API.

Exit code table

Code curl name CurlDotNet throws When How to fix
1 CURLE_UNSUPPORTED_PROTOCOL CurlUnsupportedProtocolException URL scheme with no handler (sftp://, htp:// …), a -L redirect to a non-HTTP scheme, a response that is not HTTP/1.x or HTTP/2 (HTTP/0.9 without --http0.9) Use http, https, ftp or file; --http0.9 for ancient servers
2 CURLE_FAILED_INIT CurlException, or CurlInvalidCommandException No URL at all (curl, curl -s, a CurlOptions without Url), an option is missing its value (curl -H), a numeric option has a non-numeric value (-m, --connect-timeout, --max-redirs, --retry, --retry-delay, --retry-max-time, --speed-limit, --speed-time, --limit-rate, --max-filesize; the message names the option, and seconds are read like curl so 1e1 and +5 work), a malformed --aws-sigv4 (also from AwsSigV4Config.Parse), two request methods conflict (-I with -d, -F with -d, --json with -F), -K files nested too deeply, a --variable error Fix the command as the message says
3 CURLE_URL_MALFORMAT CurlMalformedUrlException, or CurlException The URL (or a redirect Location) cannot be parsed, a malformed glob such as [1- Fix the URL; escape { [ or use -g
4 CURLE_NOT_BUILT_IN CurlNotBuiltInException --pinnedpubkey on an https transfer where the pin cannot be checked (.NET Standard 2.0 / .NET Framework, or your own HttpClient) Run on .NET 5+, or drop the pin
5 CURLE_COULDNT_RESOLVE_PROXY CurlCouldntResolveProxyException The proxy host name does not resolve Check -x and the http_proxy / https_proxy / all_proxy variables
6 CURLE_COULDNT_RESOLVE_HOST CurlCouldntResolveHostException The host name does not resolve Check the spelling and DNS; --resolve host:port:address
7 CURLE_COULDNT_CONNECT CurlCouldntConnectException Connection refused or unreachable, to the host or the proxy (ViaProxy says which); a refused -p tunnel Start the server, check port and firewall; --retry-connrefused
8 CURLE_WEIRD_SERVER_REPLY CurlWeirdServerReplyException The response head is malformed (bad status line, invalid header line) Server bug; inspect with -v
9 CURLE_REMOTE_ACCESS_DENIED CurlDotNet.Core.CurlFtpException The FTP server refused the request Check the path, -u and permissions
18 CURLE_PARTIAL_FILE CurlPartialFileException The connection closed before Content-Length bytes (or the last chunk) arrived Retry, or resume with -C -
22 CURLE_HTTP_RETURNED_ERROR CurlHttpReturnedErrorException -f / --fail or --fail-with-body and a status of 400 or more Handle the status, or drop -f to get a normal result
23 CURLE_WRITE_ERROR CurlWriteErrorException An -o, -O, -D or -c file cannot be written: missing directory, bad name, no permission, -J would overwrite Create the directory or add --create-dirs; pick a writable path
26 CURLE_READ_ERROR CurlReadErrorException An input file is missing or unreadable: -H @file, -T, -F name=@file, --data-urlencode @file, -K, --variable name@file, .netrc Fix the path (relative paths are relative to the working directory)
28 CURLE_OPERATION_TIMEDOUT CurlOperationTimeoutException -m / --max-time or --connect-timeout expired, or the transfer stayed below --speed-limit bytes per second for --speed-time seconds Raise the limit; add --retry
33 CURLE_RANGE_ERROR CurlRangeErrorException -C asked to resume but the server sent the whole document Download again without -C
35 CURLE_SSL_CONNECT_ERROR CurlSslConnectErrorException The TLS handshake failed (not a certificate problem): no common TLS version or cipher, the server rejected the client certificate, or the port does not speak TLS Check the port and the --tlsv1.x / --tls-max / --ciphers / --cert options
37 CURLE_FILE_COULDNT_READ_FILE CurlFileCouldntReadException A single -d @file, --data-binary @file or --json @file is missing (the curl tool reports 26 here); file:// permission denied Fix the path
42 CURLE_ABORTED_BY_CALLBACK CurlAbortedByCallbackException Your progress callback or CurlSettings.OnRedirect handler threw; your exception is the InnerException Handle the error inside the callback, or throw on purpose to refuse a redirect
43 CURLE_BAD_FUNCTION_ARGUMENT CurlBadFunctionArgumentException, or CurlException CR or LF inside a -H value or --request-target, -C with an upload (curl ignores -C there), #name in -o with no such glob One header per -H; drop -C for uploads
47 CURLE_TOO_MANY_REDIRECTS CurlTooManyRedirectsException More redirects than --max-redirs (default 50; -1 is unlimited) Raise --max-redirs or fix the redirect loop
49 CURLE_SETOPT_OPTION_SYNTAX CurlOptionSyntaxException A --resolve or --connect-to entry is malformed Use HOST:PORT:ADDRESS / HOST1:PORT1:HOST2:PORT2
52 CURLE_GOT_NOTHING CurlGotNothingException The server accepted the request and closed without a byte Check the server, the port and http vs https
56 CURLE_RECV_ERROR CurlReceiveErrorException The connection failed while reading the response, or the headers exceed 300 KB Retry; server-side problem
58 CURLE_SSL_CERTPROBLEM CurlSslCertificateProblemException The --cert/--key client certificate cannot be used: missing file, wrong --cert-type, wrong password, key does not match Fix the file, --cert-type or --pass
60 CURLE_PEER_FAILED_VERIFICATION CurlPeerFailedVerificationException The server certificate was rejected (expired, wrong name, untrusted CA) Trust a private CA with --cacert (.NET 5+) or in the operating system store; -k only for development
61 CURLE_BAD_CONTENT_ENCODING CurlBadContentEncodingException A --compressed body cannot be decoded, or the Transfer-Encoding is invalid Server bug; drop --compressed to get the raw bytes
63 CURLE_FILESIZE_EXCEEDED CurlFileSizeExceededException The response is larger than --max-filesize Raise the limit
77 CURLE_SSL_CACERT_BADFILE CurlSslCaCertBadFileException The --cacert file or --capath folder is missing, unreadable or holds no certificate Fix the path; use PEM or DER certificates
90 CURLE_SSL_PINNEDPUBKEYNOTMATCH CurlSslPinnedPublicKeyMismatchException The server’s public key matches no --pinnedpubkey pin, or the pin cannot be read Update the pin; the message shows the pin of the key that was presented
94 CURLE_AUTH_ERROR CurlAuthErrorException --digest, --ntlm, --negotiate, --anyauth (or a --proxy- variant) could not process the challenge Try --basic; look at the challenge with -v. A wrong password is a normal 401 result, not this error
100 CURLE_TOO_LARGE CurlTooLargeException More than 5000 response header fields, or a header line over 100 KB Server bug or hostile server

Exceptions of the same family share base types, so you can catch broadly: CurlTimeoutException for 28, CurlSslException for 35, 58, 60, 77 and 90, CurlDotNet.Exceptions.CurlHttpException for 22, and CurlException for everything.

A command with options but no URL (curl -s) throws CurlInvalidCommandException with CurlErrorCode 2, as the curl tool exits with 2.

When does a code stop the command?

With several URLs in one command, a failed transfer does not stop the others, and the outcome is that of the last transfer, as in curl. The command-line errors 2 and 43 always stop it, and --fail-early stops at the first failure. See multiple URLs and URL globbing.

What curl would have printed

curl writes as it goes, so before it exits with an error code it may already have printed headers (-i), an error page (--fail-with-body), earlier URLs’ bodies and -w text. CurlDotNet attaches that text to the exception: ex.StandardOutput is the complete output, and ex.Data[CurlException.WriteOutDataKey] the -w part alone.

using CurlDotNet;
using CurlDotNet.Exceptions;

try
{
    await Curl.ExecuteAsync("curl -i -f -w '\\nexit=%{exitcode}' https://httpbin.org/status/418");
}
catch (CurlHttpReturnedErrorException ex)
{
    Console.WriteLine(ex.StandardOutput);   // HTTP/1.1 418 ... headers, then "exit=22"
}

Switch on the code

using CurlDotNet;
using CurlDotNet.Exceptions;

try
{
    await Curl.ExecuteAsync("curl -f --connect-timeout 5 https://httpbin.org/get");
}
catch (CurlException ex)
{
    var advice = ex.CurlErrorCode switch
    {
        6 or 5 => "Check the host or proxy name.",
        7 => "The server is not accepting connections.",
        28 => "Too slow: raise -m or add --retry.",
        35 or 60 => "TLS problem: check the certificate and the port.",
        22 => "The server answered with an error status.",
        _ => ex.Message
    };
    Console.WriteLine(advice);
}

Codes CurlDotNet does not produce

curl defines more than 90 codes. The ones not in the table belong to protocols or features CurlDotNet does not implement (SSH, SMTP, TFTP, TELNET, LDAP) and are never returned. Some exception types exist for them so that code written against curl’s numbering compiles; the error reference lists every type, including those reserved ones.