curl/curl ยท error

curl_easy_perform() failed: %s

Error message

curl_easy_perform() failed: %s

What it means

Generic failure report from curl_easy_perform() in the websocket-updown example; result holds the CURLcode returned by the transfer. The message just renders curl_easy_strerror(result), so the underlying cause is whatever made the full WebSocket upload+download transfer fail.

Source

Thrown at docs/examples/websocket-updown.c:120

      curl_easy_setopt(curl, CURLOPT_URL, argv[1]);
    else
      curl_easy_setopt(curl, CURLOPT_URL, "wss://example.com");

    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl);
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_cb);
    /* tell curl that we want to send the payload */
    rctx.curl = curl;
    rctx.blen = sizeof(payload) - 1;
    memcpy(rctx.buf, payload, rctx.blen);
    curl_easy_setopt(curl, CURLOPT_READDATA, &rctx);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

    /* Perform the request, result gets the return code */
    result = curl_easy_perform(curl);
    /* Check for errors */
    if(result != CURLE_OK)
      fprintf(stderr, "curl_easy_perform() failed: %s\n",
              curl_easy_strerror(result));

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  curl_global_cleanup();
  return (int)result;
}

View on GitHub (pinned to 527573490e)

Solutions

  1. Read the rendered curl_easy_strerror text (e.g. 'Couldn\'t connect to server', 'SSL peer certificate...') to narrow the cause.
  2. Verify the wss:// URL resolves and that the endpoint supports WebSocket (expect HTTP 101 on upgrade).
  3. Check TLS: set CURLOPT_CAINFO to a valid CA bundle or fix the server certificate; confirm CURLOPT_SSL_VERIFYPEER behavior.
  4. Enable CURLOPT_VERBOSE to log the handshake and identify where the transfer aborts.
  5. Ensure the libcurl in use was built with WebSocket support.

Example fix

// before
result = curl_easy_perform(curl);
if(result != CURLE_OK)
  fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(result));
// after
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
result = curl_easy_perform(curl);
if(result != CURLE_OK)
  fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(result));
Defensive patterns

Strategy: retry

Validate before calling

/* Pre-flight removes the argument/config class of failures.
   Transient network failures are still handled by the retry loop. */
static CURLcode ws_perform_preflight(CURL *curl, const char *url) {
  if(curl == NULL) return CURLE_BAD_FUNCTION_ARGUMENT;
  if(url == NULL)  return CURLE_URL_MALFORMAT;
  if(strncmp(url, "ws://", 5) && strncmp(url, "wss://", 6))
      return CURLE_UNSUPPORTED_PROTOCOL;
  return CURLE_OK;
}

Type guard

/* Narrow a CURLcode into an actionable bucket; drives retry policy. */
enum curl_kind { K_OK, K_TRANSIENT, K_PERMANENT };
static enum curl_kind curlcode_classify(CURLcode c) {
  switch(c) {
    case CURLE_OK:                       return K_OK;
    case CURLE_COULDNT_RESOLVE_HOST:
    case CURLE_COULDNT_RESOLVE_PROXY:
    case CURLE_COULDNT_CONNECT:
    case CURLE_WEIRD_SERVER_REPLY:
    case CURLE_OPERATION_TIMEDOUT:
    case CURLE_GOT_NOTHING:
    case CURLE_SEND_ERROR:
    case CURLE_RECV_ERROR:
    case CURLE_PARTIAL_FILE:
    case CURLE_HTTP2_STREAM:
    case CURLE_AGAIN:                    return K_TRANSIENT;
    default:                             return K_PERMANENT;
  }
}

Try / catch

CURLcode r;
for(unsigned attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
    r = curl_easy_perform(curl);
    if(curlcode_classify(r) != K_TRANSIENT) break;   /* OK or permanent */
    backoff_sleep(attempt);                           /* exp backoff + jitter */
}
if(r != CURLE_OK)
    fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(r));

Prevention

When it happens

Trigger: Emitted at the end of main() when curl_easy_perform(curl) returns non-CURLE_OK for the wss:// upload/download transfer that has read_cb (upload) and write_cb (download) attached.

Common situations: DNS/TCP/TLS connection failures, invalid or unreachable ws/wss URL, server rejecting the WebSocket upgrade, expired/bad TLS certificates, proxy misconfiguration, timeouts, or a libcurl build without WebSocket support.

Related errors


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