curl/curl ยท error

curl_multi_waitfds() failed, code %d.

Error message

curl_multi_waitfds() failed, code %d.

What it means

Printed in the curl_multi_waitfds example on the first (probe) call curl_multi_waitfds(multi, NULL, 0, &fd_count), which is the documented way to discover how many descriptors the multi handle uses before allocating storage. A non-zero CURLMcode means even this size query failed. The %d is the CURLMcode. curl_multi_waitfds was added in libcurl 8.8.0.

Source

Thrown at docs/libcurl/curl_multi_waitfds.md:79

#include <stdlib.h>

int main(void)
{
  CURLMcode mresult;
  struct curl_waitfd *ufds;

  CURLM *multi = curl_multi_init();

  do {
    /* call curl_multi_perform() */

    /* get the count of file descriptors from the transfers */
    unsigned int fd_count = 0;

    mresult = curl_multi_waitfds(multi, NULL, 0, &fd_count);

    if(mresult != CURLM_OK) {
      fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mresult);
      break;
    }

    if(!fd_count)
      continue; /* no descriptors yet */

    /* allocate storage for our descriptors */
    ufds = malloc(fd_count * sizeof(struct curl_waitfd));

    /* get wait descriptors from the transfers and put them into array. */
    mresult = curl_multi_waitfds(multi, ufds, fd_count, &fd_count);

    if(mresult != CURLM_OK) {
      fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mresult);
      free(ufds);
      break;
    }

View on GitHub (pinned to 527573490e)

Solutions

  1. Check libcurl version at build/run time (need >= 8.8.0) since curl_multi_waitfds did not exist before then.
  2. Verify curl_multi_init() returned a valid, non-NULL CURLM* and that it has not been cleaned up.
  3. Ensure the call is not made from inside a libcurl callback (CURLM_RECURSIVE_API_CALL).
  4. On CURLM_OUT_OF_MEMORY, investigate overall process memory; on CURLM_INTERNAL_ERROR, upgrade libcurl and report the bug.

Example fix

// before
mresult = curl_multi_waitfds(multi, NULL, 0, &fd_count);
if(mresult != CURLM_OK) {
  fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", mresult);
  break;
}

// after - guard handle and version, report symbolically
if(!multi) { fprintf(stderr, "multi handle is NULL\n"); break; }
mresult = curl_multi_waitfds(multi, NULL, 0, &fd_count);
if(mresult != CURLM_OK) {
  fprintf(stderr, "curl_multi_waitfds() probe failed: %s (%d)\n",
          curl_multi_strerror(mresult), mresult);
  break;
}
Defensive patterns

Strategy: validation

Validate before calling

// First call: query the descriptor count with a NULL buffer.
if (multi == NULL) { return -1; }
unsigned int fd_count = 0;
/* size == 0 is the documented way to ask for the count. */
CURLMcode q = curl_multi_waitfds(multi, NULL, 0, &fd_count);
if (q != CURLM_OK || fd_count == 0) { /* nothing to wait on yet */ }

Type guard

// Validate the count probe before allocating.
static bool fd_count_usable(unsigned int n) {
    return n > 0 && n < (1024u * 1024u); /* sanity ceiling against runaway */
}

Try / catch

mresult = curl_multi_waitfds(multi, NULL, 0, &fd_count);
if (mresult != CURLM_OK) {
    fprintf(stderr, "curl_multi_waitfds() failed, code %d.\n", (int)mresult);
    break;
}

Prevention

When it happens

Trigger: curl_multi_waitfds(multi, NULL, 0, &fd_count) returns CURLM_BAD_HANDLE (1) when multi is NULL, freed, or not a CURLM handle; CURLM_OUT_OF_MEMORY (3); or CURLM_INTERNAL_ERROR (4). Because size=0 is explicitly the supported probe path, an OUT_OF_MEMORY here indicates genuine memory pressure rather than a too-small buffer.

Common situations: Calling curl_multi_waitfds on a libcurl older than 8.8.0 where the symbol is absent (link-time or runtime symbol error, not a CURLMcode but worth checking); passing a handle before curl_multi_init or after curl_multi_cleanup; running under severe memory pressure; calling from within a callback (CURLM_RECURSIVE_API_CALL).

Related errors


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