Skip to content

[BUG] SessionState::TimedOut is never dispatched, so every timeout surfaces as SendFailed #4604

Description

@mateenali66

Describe your environment

main at d64b2512, macOS 26.6.2, Apple clang, CMake Debug build with WITH_ELASTICSEARCH=ON, libcurl 8.7.1. The curl client is the only HTTP client in tree.

Steps to reproduce

Point the Elasticsearch log exporter at a socket that accepts the connection and never answers, with a 1 second response timeout.

ElasticsearchExporterOptions options("127.0.0.1", port, "logs", /*response_timeout=*/1);
ElasticsearchLogRecordExporter exporter(options);
auto record = exporter.MakeRecordable();
record->SetBody("probe");
exporter.Export(nostd::span<std::unique_ptr<sdklogs::Recordable>>(&record, 1));
s = socket.socket(); s.bind(("127.0.0.1", port)); s.listen(8)
while True: c, _ = s.accept()   # accept, read nothing, answer nothing

What is the expected behavior?

SetTimeoutMs() expires, the session reports SessionState::TimedOut, and the exporter logs [ES Log Exporter] Request timed out from the handler at es_log_record_exporter.cc:178. The doc comment at :114 states this directly: the longest wait is "the timeout of the request, set by SetTimeoutMs(), which arrives here as a TimedOut session event".

What is the actual behavior?

SendFailed, every time. Two runs:

log +     0.4ms  [ES Log Exporter] Sending request
log +  1006.4ms  [ES Log Exporter] Failed to send request
export 0 result=FAILURE elapsed=1006.4ms
log +  1002.9ms  [ES Log Exporter] Failed to send request
export 1 result=FAILURE elapsed=1003.0ms

The timeout is honoured, so this is a classification problem rather than a hang.

Additional context

SessionState::TimedOut is declared at ext/include/opentelemetry/ext/http/client/http_client.h:93 and is never dispatched anywhere under ext/. HttpOperation::PerformCurlMessage (http_operation_curl.cc:1564-1594) maps every non-CURLE_OK code to ConnectFailed when the state is Connecting and to SendFailed when it is Connected or Sending, so CURLE_OPERATION_TIMEDOUT is indistinguishable from a refused connection or a dropped socket.

Four handler branches wait for an event that cannot arrive: es_log_record_exporter.cc:178 and :320, otlp_http_client.cc:200 and :303. A caller cannot currently tell a timeout from a transport failure, which matters because the two usually deserve different retry behaviour.

Two ways out, and I do not want to guess which you prefer. Dispatch TimedOut for CURLE_OPERATION_TIMEDOUT in PerformCurlMessage, which makes the four branches live and could change behaviour for anyone switching on SendFailed today. Or drop the enum value with its four branches and correct the comment at :114. Happy to send a PR either way.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIndicates an issue or PR lacks a `triage/foo` label and requires one.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions