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

  1. 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
  2. Verify host reachability and DNS for the URL set via curl_url_set / CURLOPT_CURLU
  3. Inspect the CURLcode (curl_easy_strerror) and enable CURLOPT_VERBOSE (already set in the example) to see the exact stage that failed
  4. 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

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


AI-assisted analysis of curl/curl@527573490e (2026-08-01). Data as JSON: /data/errors/1f3090468e04c3f0.json. Report an issue: GitHub.