#19095·esp-idf

esp_http_server: logging can clobber errno and turn EAGAIN into a fatal send error (IDFGH-18290)

Author: mournerCreated Sep 16, 2026Updated Sep 16, 2026
LabelsStatus: Opened

Answers checklist.

  • I have read the relevant ESP-IDF Programming Guide documentation and the issue is not addressed there.
  • I have updated my IDF branch to the latest version and checked that the issue is present there.
  • I have searched the issue tracker and not found a report of this errno-clobbering issue.

Tested on v6.1. The same log-before-switch code is present in upstream master, but I haven't rerun the firmware on master.

IDF version.

v6.1 (fff9895c82d744c7237be8847347bdd1b07c6643)

Espressif SoC revision.

ESP32-S3, revision v0.2

Operating System used.

macOS (Apple Silicon)

How did you build your project?

Command line with idf.py

Development Kit.

ESP32-S3-DevKitC-1 N16R8

Power Supply used.

USB

What is the expected behavior?

When a nonblocking send would block, httpd_socket_send() should return HTTPD_SOCK_ERR_TIMEOUT, allowing the application to retry.

What is the actual behavior?

It returns HTTPD_SOCK_ERR_FAIL. My SSE server consequently closes healthy clients, which reconnect every ~3 seconds. This starts when the backlog exceeds the 5,760-byte TCP send buffer; smaller backlogs work.

Steps to reproduce.

  1. Use UART as the primary console and USB Serial/JTAG as the secondary console, with the secondary USB connection absent:

    ini
    CONFIG_ESP_CONSOLE_UART_DEFAULT=y
    CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y
    CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760
  2. With warning logs enabled, send an SSE backlog larger than the TCP send buffer using httpd_socket_send(server, fd, data, remaining, MSG_DONTWAIT), advancing by each successful partial write.

  3. When the buffer fills, inspect errno before and after the warning in httpd_sock_err().

Debug Logs.

Observed values from instrumentation (summarized, not a raw log):

Bytes sent before failure: 5760
Underlying send(): -1, errno = 11 (EAGAIN)
Warning prints: error in send : 11
errno at the following switch: 5 (EIO)
httpd_socket_send(): HTTPD_SOCK_ERR_FAIL

More Information.

In httpd_sock_err(), ESP_LOGW(..., errno) runs before switch (errno).

The console writes to both outputs. The secondary usb_serial_jtag_write() sets errno = EIO when disconnected, and the console writer doesn't restore it. The warning can therefore print EAGAIN over UART while leaving EIO for the switch.

Calling send() directly and immediately capturing errno, then retrying on EAGAIN/EWOULDBLOCK/EINTR, fixed the issue: a forced ~24 KB replay delivered all 25,427 bytes without disconnecting.

Could httpd_sock_err() save errno before logging and use that saved value for both the warning and the switch? The helper is shared by the default send and receive paths.