curl/curl ยท error
curl_easy_perform() failed: %s
Error message
curl_easy_perform() failed: %s
What it means
Printed by the urlapi.c example when curl_easy_perform() returns a non-CURLE_OK code while performing a transfer against the CURLU-configured URL. The %s is curl_easy_strerror(result). This is the standard transfer-failure path and occurs after the URL was successfully parsed by curl_url_set. It indicates the transfer itself (DNS/connect/TLS/HTTP) failed, not the URL setup.
Source
Thrown at docs/examples/urlapi.c:68
if(uc) {
fprintf(stderr, "curl_url_set() failed: %s", curl_url_strerror(uc));
goto cleanup;
}
/* get a curl handle */
curl = curl_easy_init();
if(curl) {
/* set urlp to use as working URL */
curl_easy_setopt(curl, CURLOPT_CURLU, urlp);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
/* only allow HTTP, TFTP and SFTP */
curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, "http,tftp,sftp");
result = curl_easy_perform(curl);
/* Check for errors */
if(result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(result));
}
cleanup:
curl_url_cleanup(urlp);
curl_easy_cleanup(curl);
curl_global_cleanup();
return (int)result;
}
View on GitHub (pinned to 527573490e)
Solutions
- Ensure the URL scheme is included in CURLOPT_PROTOCOLS_STR (e.g. add 'https' if the CURLU URL is https), since a mismatch yields CURLE_UNSUPPORTED_PROTOCOL
- Verify host reachability and DNS for the URL set via curl_url_set / CURLOPT_CURLU
- Inspect the CURLcode (curl_easy_strerror) and enable CURLOPT_VERBOSE (already set in the example) to see the exact stage that failed
- Confirm curl_easy_setopt(curl, CURLOPT_CURLU, urlp) is called before curl_easy_perform() so the parsed URL is actually applied
Example fix
// before
curl_easy_setopt(curl, CURLOPT_CURLU, urlp);
curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, "http,tftp,sftp");
result = curl_easy_perform(curl);
if(result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(result));
// after: allow the scheme actually present in the URL and log the code
curl_easy_setopt(curl, CURLOPT_CURLU, urlp);
curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, "http,https,tftp,sftp");
result = curl_easy_perform(curl);
if(result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s (code %d)\n",
curl_easy_strerror(result), result); Defensive patterns
Strategy: try-catch
Validate before calling
/* Validate URL + options once before perform; cheap and removes most 128 causes. */
static int preflight(CURL *c, const char *url) {
if (!url || !*url) return 0;
CURLU *u = curl_url();
CURLUcode uc = curl_url_set(u, CURLUPART_URL, url, CURLU_DEFAULT_SCHEME);
curl_url_cleanup(u);
if (uc != CURLUE_OK) return 0;
curl_easy_setopt(c, CURLOPT_URL, url);
curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT_MS, 5000L);
curl_easy_setopt(c, CURLOPT_TIMEOUT_MS, 30000L);
curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
return 1;
}
if (!preflight(curl, argv[1])) { fprintf(stderr, "preflight failed\n"); exit(EXIT_FAILURE); } Type guard
/* Classify the perform() CURLcode so retry policy is data-driven. */
typedef enum { XFER_OK, XFER_RETRY, XFER_FAIL } xfer_t;
static xfer_t classify(CURLcode rc) {
switch (rc) {
case CURLE_OK: return XFER_OK;
case CURLE_COULDNT_CONNECT:
case CURLE_OPERATION_TIMEDOUT:
case CURLE_SEND_ERROR:
case CURLE_RECV_ERROR:
case CURLE_GOT_NOTHING: return XFER_RETRY;
default: return XFER_FAIL;
}
} Try / catch
CURLcode rc;
int attempt = 0;
do {
rc = curl_easy_perform(curl);
if (classify(rc) == XFER_RETRY && attempt < 3) {
usleep(200000L << attempt); /* 0.2,0.4,0.8s backoff */
continue;
}
break;
} while (++attempt);
if (rc != CURLE_OK) {
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(rc));
curl_easy_cleanup(curl);
return EXIT_FAILURE;
} Prevention
- Always set CURLOPT_CONNECTTIMEOUT_MS and CURLOPT_TIMEOUT_MS; the most common perform() 'failure' is an indefinite hang mistaken for a hard error.
- Classify the CURLcode and only retry genuinely transient codes (connect/timeout/send/recv); do not retry malformed-URL or auth errors.
- After perform, read CURLINFO_RESPONSE_CODE and treat 4xx/5xx as application errors even when curl returns CURLE_OK.
- For HTTPS, set CURLOPT_CAINFO to a known bundle and CURLOPT_SSL_VERIFYPEER=1; SSL handshake failures are a frequent source of perform() errors.
When it happens
Trigger: curl_easy_perform() is called after setting CURLOPT_CURLU to the parsed URL and restricting protocols with CURLOPT_PROTOCOLS_STR to 'http,tftp,sftp'; failure occurs if the host is unreachable, the protocol is blocked by the allow-list, DNS resolution fails, or the server returns a connection-level error.
Common situations: Setting CURLOPT_PROTOCOLS_STR to a scheme not matching the CURLU URL's scheme (e.g. an https URL when only http/tftp/sftp are allowed yields CURLE_UNSUPPORTED_PROTOCOL); pointing at an unreachable host; a typo in the URL host; TLS/cert issues if the scheme were https; restricting protocols too aggressively.
Related errors
- Transfer failed: (%d) %s
- curl_easy_perform() failed: %s
- curl_easy_perform() failed: %s
- curl_easy_perform() failed: %s
- curl_easy_perform() failed: %s
AI-assisted analysis of curl/curl@527573490e (2026-08-01).
Data as JSON: /data/errors/1f3090468e04c3f0.json.
Report an issue: GitHub.