Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package haproxy for openSUSE:Factory checked 
in at 2026-06-27 18:05:32
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/haproxy (Old)
 and      /work/SRC/openSUSE:Factory/.haproxy.new.11887 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "haproxy"

Sat Jun 27 18:05:32 2026 rev:188 rq:1361802 version:3.4.1+git0.3e888a769

Changes:
--------
--- /work/SRC/openSUSE:Factory/haproxy/haproxy.changes  2026-06-22 
17:43:18.334918732 +0200
+++ /work/SRC/openSUSE:Factory/.haproxy.new.11887/haproxy.changes       
2026-06-27 18:06:40.133131192 +0200
@@ -1,0 +2,41 @@
+Thu Jun 25 15:14:23 UTC 2026 - Marcus Rueckert <[email protected]>
+
+- Update to version 3.4.1+git0.3e888a769:
+  * [RELEASE] Released version 3.4.1
+
+-------------------------------------------------------------------
+Thu Jun 25 09:32:10 UTC 2026 - Marcus Rueckert <[email protected]>
+
+- Update to version 6344dfa:
+  * MINOR: otel: test: documented the context name header prefix in fe/be tests
+  * BUG/MINOR: otel: fixed the inject '-' autoname and its header prefix
+  * CLEANUP: otel: cast discarded prune_acl/pool_destroy returns to void
+  * MINOR: otel: returned ERR_WARN from the scope location checks
+  * DOC: otel: reconciled the README manual with the keyword reference
+  * DOC: otel: corrected the inline-link limit in the span reference
+  * DOC: otel: documented the metric create/update value-binding model
+  * MINOR: otel: demoted advisory config diagnostics to warnings
+  * MINOR: otel: waited for in-progress instrument creation before recording
+  * MINOR: otel: coerced string instrument values, warning only on failure
+  * MINOR: otel: unified the configuration parser checks and messages
+  * MINOR: otel: guarded flt_otel_pool_alloc against oversize requests
+  * DOC: otel: corrected the pool-allocator and ACL-resolution descriptions
+
+-------------------------------------------------------------------
+Thu Jun 25 09:32:09 UTC 2026 - Marcus Rueckert <[email protected]>
+
+- Update to version 3.4.0+git43.e5221ba2b:
+  * BUG/MEDIUM: mux-quic: Drain the given amount of data in 
qcs_http_reset_buf()
+  * BUG/MEDIUM: mux-spop: Truly drain outgoing data when the stream is closed
+  * BUG/MEDIUM: mux-h2: Truly drain outgoing HTX data when the stream is closed
+  * BUG/MEDIUM: mux-fcgi: Truly drain outgoing HTX data when the stream is 
closed
+  * REGTESTS: quic: test H3 request without content-length
+  * BUG/MEDIUM: h3: increment unknown request payload length
+  * DOC: lua: remove incorrect init tags
+  * BUG/MINOR: hq-interop: support response buffer wrapping
+  * BUG/MINOR: hq-interop: support full demux buf on large response
+  * BUG/MEDIUM: hlua: Properly report EOS when http applet exits
+  * BUG/MINOR: hq-interop: prevent reset if missing content-length
+  * BUG/MINOR: hq-interop: reject too big content
+
+-------------------------------------------------------------------

Old:
----
  haproxy-3.4.0+git31.fc300e9f2.tar.gz
  haproxy-opentelemetry-ce86d09.tar.gz

New:
----
  haproxy-3.4.1+git0.3e888a769.tar.gz
  haproxy-opentelemetry-6344dfa.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ haproxy.spec ++++++
--- /var/tmp/diff_new_pack.2bDr9v/_old  2026-06-27 18:06:41.057162405 +0200
+++ /var/tmp/diff_new_pack.2bDr9v/_new  2026-06-27 18:06:41.057162405 +0200
@@ -44,10 +44,10 @@
 %bcond_with opentelemetry
 
 Name:           haproxy
-Version:        3.4.0+git31.fc300e9f2
+Version:        3.4.1+git0.3e888a769
 Release:        0
 %if %{with opentelemetry}
-%global otel_revision ce86d09
+%global otel_revision 6344dfa
 %global otel_subdir   haproxy-opentelemetry-%{otel_revision}
 %global otel_additional_source -a9
 %endif

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.2bDr9v/_old  2026-06-27 18:06:41.133164972 +0200
+++ /var/tmp/diff_new_pack.2bDr9v/_new  2026-06-27 18:06:41.141165242 +0200
@@ -1,11 +1,11 @@
 <servicedata>
   <service name="tar_scm">
     <param name="url">http://git.haproxy.org/git/haproxy-3.4.git/</param>
-    <param 
name="changesrevision">fc300e9f2ce352516341503e8bc62f4c0b429627</param>
+    <param 
name="changesrevision">3e888a769ec9720a3cf32b28b4401af49882c861</param>
   </service>
   <service name="tar_scm">
     <param 
name="url">https://github.com/haproxytech/haproxy-opentelemetry.git/</param>
-    <param 
name="changesrevision">ce86d09e9d9b41d73212f5081cf7f4f74b6ff28d</param>
+    <param 
name="changesrevision">6344dfaba04acfbf2e53f381f08decefc9ccbe35</param>
   </service>
 </servicedata>
 (No newline at EOF)

++++++ haproxy-3.4.0+git31.fc300e9f2.tar.gz -> 
haproxy-3.4.1+git0.3e888a769.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/CHANGELOG 
new/haproxy-3.4.1+git0.3e888a769/CHANGELOG
--- old/haproxy-3.4.0+git31.fc300e9f2/CHANGELOG 2026-06-18 09:19:41.000000000 
+0200
+++ new/haproxy-3.4.1+git0.3e888a769/CHANGELOG  2026-06-25 15:54:08.000000000 
+0200
@@ -1,6 +1,51 @@
 ChangeLog :
 ===========
 
+2026/06/25 : 3.4.1
+    - BUG/MEDIUM: check: Skip tcpcheck post-config for external checks
+    - BUG/MEDIUM: check: Ignore small-buffer option when starting an external 
check
+    - MINOR: check: Don't dump buffers state in check traces for external 
checks
+    - BUG/MEDIUM: server/checks: Support healtcheck keyword on default-server 
lines
+    - BUG/MEDIUM: mux_quic: prevent risk of infinite loop on recv
+    - BUG/MINOR: mux_quic: do not interrupt recv on error/incomplete data
+    - BUG/MINOR: tcpcheck: Override external check if healthcheck section is 
set
+    - REGTESTS: checks: Add script for external healthchecks
+    - BUG/MEDIUM: regex: initialize the match array earlier during boot
+    - BUG/MEDIUM: threads: Fiw build when using no thread
+    - BUG/MEDIUM: xprt_qmux: implement ->get_ssl_sock_ctx() to get the SSL laye
+    - CLEANUP: sessions: simplify the sess_priv_conns pool name
+    - BUG/MINOR: acl: report "ACL" not "map" in ACL ID lookup failures
+    - BUG/MEDIUM: checks: Dequeue checks on purge
+    - REGTESTS: Fix log matching in healthcheck-section.vtc
+    - BUG/MINOR: quic: fix Initial length value in sent packets
+    - BUG/MEDIUM: acme: stuck ACME task when authz is already "valid"
+    - BUG/MEDIUM: ktls: defer enabling TLS ULP on a socket until connected
+    - MINOR: errors: add ha_diag_notice() to report diag-level notifications
+    - BUG/MINOR: cpu-topo: use ha_diag_notice() to report thread creations
+    - BUG/MEDIUM: h3: Properly handle PUSH_PROMISE on backend connections
+    - BUG/MINOR: server: fix add server with consistent hash balancing
+    - BUG/MINOR: http-ana: Remove a debugging memset on redirect
+    - BUG/MEDIUM: http-ana: Don't ignore L7 retry errors
+    - BUG/MINOR: mux-h1: Properly resolve file path for 'h1-case-adjust-file'
+    - BUG/MINOR: quic: fix rxbuf settings on backend side
+    - BUG/MEDIUM: ssl: Don't free the early data buffer too early
+    - BUG/MINOR: hpack-tbl: add missing NULL check after hpack_dht_defrag()
+    - BUG/MEDIUM: mux_quic: fix freeze transfer after QCS rxbuf realign
+    - BUG/MEDIUM: http-act: Make a copy of the sample expr in 
(set/add)-headers-bin
+    - BUG/MEDIUM: mux-fcgi: fix uint16_t overflow in drl += drp
+    - BUG/MINOR: hq-interop: reject too big content
+    - BUG/MINOR: hq-interop: prevent reset if missing content-length
+    - BUG/MEDIUM: hlua: Properly report EOS when http applet exits
+    - BUG/MINOR: hq-interop: support full demux buf on large response
+    - BUG/MINOR: hq-interop: support response buffer wrapping
+    - DOC: lua: remove incorrect init tags
+    - BUG/MEDIUM: h3: increment unknown request payload length
+    - REGTESTS: quic: test H3 request without content-length
+    - BUG/MEDIUM: mux-fcgi: Truly drain outgoing HTX data when the stream is 
closed
+    - BUG/MEDIUM: mux-h2: Truly drain outgoing HTX data when the stream is 
closed
+    - BUG/MEDIUM: mux-spop: Truly drain outgoing data when the stream is closed
+    - BUG/MEDIUM: mux-quic: Drain the given amount of data in 
qcs_http_reset_buf()
+
 2026/06/03 : 3.4.0
     - BUG/MINOR: tcpcheck: Check LDAP response to not read more data than 
available
     - BUG/MINOR: ssl-gencert: validate SNI characters to prevent SAN 
certificate injection
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/VERDATE 
new/haproxy-3.4.1+git0.3e888a769/VERDATE
--- old/haproxy-3.4.0+git31.fc300e9f2/VERDATE   2026-06-18 09:19:41.000000000 
+0200
+++ new/haproxy-3.4.1+git0.3e888a769/VERDATE    2026-06-25 15:54:08.000000000 
+0200
@@ -1,2 +1,2 @@
 $Format:%ci$
-2026/06/03
+2026/06/25
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/VERSION 
new/haproxy-3.4.1+git0.3e888a769/VERSION
--- old/haproxy-3.4.0+git31.fc300e9f2/VERSION   2026-06-18 09:19:41.000000000 
+0200
+++ new/haproxy-3.4.1+git0.3e888a769/VERSION    2026-06-25 15:54:08.000000000 
+0200
@@ -1 +1 @@
-3.4.0
+3.4.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/doc/configuration.txt 
new/haproxy-3.4.1+git0.3e888a769/doc/configuration.txt
--- old/haproxy-3.4.0+git31.fc300e9f2/doc/configuration.txt     2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/doc/configuration.txt      2026-06-25 
15:54:08.000000000 +0200
@@ -3,7 +3,7 @@
                           Configuration Manual
                          ----------------------
                               version 3.4
-                              2026/06/03
+                              2026/06/25
 
 
 This document covers the configuration language as implemented in the version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/doc/lua-api/index.rst 
new/haproxy-3.4.1+git0.3e888a769/doc/lua-api/index.rst
--- old/haproxy-3.4.0+git31.fc300e9f2/doc/lua-api/index.rst     2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/doc/lua-api/index.rst      2026-06-25 
15:54:08.000000000 +0200
@@ -883,7 +883,7 @@
 
 .. js:function:: core.tcp()
 
-  **context**: init, task, action
+  **context**: task, action
 
   This function returns a new object of a *socket* class.
 
@@ -891,7 +891,7 @@
 
 .. js:function:: core.httpclient()
 
-  **context**: init, task, action
+  **context**: task, action
 
   This function returns a new object of a *httpclient* class. An *httpclient*
   object must be used to process one and only one request. It must never be
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/haproxy-3.4.0+git31.fc300e9f2/reg-tests/quic/h3_unknown_length_req.vtc 
new/haproxy-3.4.1+git0.3e888a769/reg-tests/quic/h3_unknown_length_req.vtc
--- old/haproxy-3.4.0+git31.fc300e9f2/reg-tests/quic/h3_unknown_length_req.vtc  
1970-01-01 01:00:00.000000000 +0100
+++ new/haproxy-3.4.1+git0.3e888a769/reg-tests/quic/h3_unknown_length_req.vtc   
2026-06-25 15:54:08.000000000 +0200
@@ -0,0 +1,60 @@
+varnishtest "HTTP/3 request body without content-length to H1 backend"
+
+feature ignore_unknown_macro
+
+# QUIC backend are not supported with USE_QUIC_OPENSSL_COMPAT
+feature cmd "$HAPROXY_PROGRAM -cc 'feature(QUIC) && 
!feature(QUIC_OPENSSL_COMPAT) && !feature(OPENSSL_WOLFSSL)'"
+
+server s1 {
+       rxreq
+       expect req.method == "POST"
+       expect req.bodylen == 50000
+       txresp
+} -start
+
+haproxy ha_h3srv -conf {
+       global
+               .if feature(THREAD)
+                       thread-groups 1
+               .endif
+
+       defaults
+               mode http
+               timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+               timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+               timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+       listen h3_fe
+               bind "quic+fd@${fe_quic}" ssl crt ${testdir}/certs/common.pem
+               server srv ${s1_addr}:${s1_port}
+} -start
+
+haproxy ha_h3cli -conf {
+       global
+               expose-experimental-directives
+               .if feature(THREAD)
+                       thread-groups 1
+               .endif
+
+       defaults
+               mode http
+               timeout connect "${HAPROXY_TEST_TIMEOUT-5s}"
+               timeout client  "${HAPROXY_TEST_TIMEOUT-5s}"
+               timeout server  "${HAPROXY_TEST_TIMEOUT-5s}"
+
+       frontend fe
+               bind "fd@${fe}"
+               default_backend be_h3
+
+       backend be_h3
+               server h3 
quic4@${ha_h3srv_fe_quic_addr}:${ha_h3srv_fe_quic_port} ssl verify none
+} -start
+
+client c1 -connect ${ha_h3cli_fe_sock} {
+       txreq -method POST -hdr "Transfer-Encoding: chunked"
+       chunkedlen 25000
+       chunkedlen 25000
+       chunkedlen 0
+       rxresp
+       expect resp.status == 200
+} -run
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/h3.c 
new/haproxy-3.4.1+git0.3e888a769/src/h3.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/h3.c  2026-06-18 09:19:41.000000000 
+0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/h3.c   2026-06-25 15:54:08.000000000 
+0200
@@ -1875,7 +1875,7 @@
                                }
                                else if (qcs->sd) {
                                        /* content-length not present, update 
estimated payload length. */
-                                       qcs->sd->kip = h3s->data_len;
+                                       qcs->sd->kip += flen;
                                }
                        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/hlua.c 
new/haproxy-3.4.1+git0.3e888a769/src/hlua.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/hlua.c        2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/hlua.c 2026-06-25 15:54:08.000000000 
+0200
@@ -11580,6 +11580,7 @@
                res_htx->flags |= HTX_FL_EOM;
                htx_to_buf(res_htx, outbuf);
                applet_set_eoi(ctx);
+               applet_set_eos(ctx);
                http_ctx->flags |= APPLET_RSP_SENT;
        }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/hq_interop.c 
new/haproxy-3.4.1+git0.3e888a769/src/hq_interop.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/hq_interop.c  2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/hq_interop.c   2026-06-25 
15:54:08.000000000 +0200
@@ -39,12 +39,22 @@
        }
 
        if (!data || !HTTP_IS_SPHT(*ptr)) {
+               if (b_size(b) - b_room(b) >= qcm_stream_rx_bufsz()) {
+                       fprintf(stderr, "content too big\n");
+                       return -1;
+               }
+
                fprintf(stderr, "truncated stream\n");
                return 0;
        }
 
        ptr++;
        if (!--data) {
+               if (b_size(b) - b_room(b) >= qcm_stream_rx_bufsz()) {
+                       fprintf(stderr, "content too big\n");
+                       return -1;
+               }
+
                fprintf(stderr, "truncated stream\n");
                return 0;
        }
@@ -62,6 +72,11 @@
        }
 
        if (!data) {
+               if (b_size(b) - b_room(b) >= qcm_stream_rx_bufsz()) {
+                       fprintf(stderr, "content too big\n");
+                       return -1;
+               }
+
                fprintf(stderr, "truncated stream\n");
                return 0;
        }
@@ -102,9 +117,10 @@
        struct buffer *htx_buf;
        const struct stream *strm = __sc_strm(qcs->sd->sc);
        const unsigned int flags = HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN;
-       size_t to_copy = b_data(b);
+       size_t to_copy = b_contig_data(b, 0);
        size_t htx_sent = 0;
        uint32_t htx_space;
+       char *head;
 
        htx_buf = qcc_get_stream_rxbuf(qcs);
        BUG_ON(!htx_buf);
@@ -130,21 +146,43 @@
                }
        }
        else {
-               BUG_ON(b_head(b) + to_copy > b_wrap(b)); /* TODO */
-
+               head = b_head(b);
+ retry:
                htx_space = htx_free_data_space(htx);
+               if (!htx_space) {
+                       qcs->flags |= QC_SF_DEM_FULL;
+                       goto out;
+               }
+
                if (to_copy > htx_space) {
                        to_copy = htx_space;
                        fin = 0;
                }
 
+               if (b_head(b) + to_copy > b_wrap(b)) {
+                       size_t contig = b_wrap(b) - head;
+                       htx_sent = htx_add_data(htx, ist2(b_head(b), contig));
+                       if (htx_sent < contig) {
+                               qcs->flags |= QC_SF_DEM_FULL;
+                               goto out;
+                       }
+
+                       to_copy -= contig;
+                       head = b_orig(b);
+                       goto retry;
+               }
+
                htx_sent = htx_add_data(htx, ist2(b_head(b), to_copy));
-               BUG_ON(htx_sent < to_copy); /* TODO */
+               if (htx_sent < to_copy) {
+                       qcs->flags |= QC_SF_DEM_FULL;
+                       goto out;
+               }
 
                if (fin && to_copy == htx_sent)
                        htx->flags |= HTX_FL_EOM;
        }
 
+ out:
        htx_to_buf(htx, htx_buf);
        return htx_sent;
 }
@@ -152,9 +190,6 @@
 /* Returns the amount of decoded bytes from <b> or a negative error code. */
 static ssize_t hq_interop_rcv_buf(struct qcs *qcs, struct buffer *b, int fin)
 {
-       /* hq-interop parser does not support buffer wrapping. */
-       BUG_ON(b_data(b) != b_contig_data(b, 0));
-
        return !(qcs->qcc->flags & QC_CF_IS_BACK) ?
          hq_interop_rcv_buf_req(qcs, b, fin) :
          hq_interop_rcv_buf_res(qcs, b, fin);
@@ -260,6 +295,14 @@
 
                /* only body is transferred on HTTP/0.9 */
                case HTX_BLK_RES_SL:
+                       sl = htx_get_blk_ptr(htx, blk);
+                       if (!(sl->flags & HTX_SL_F_XFER_LEN))
+                               qcs->flags |= QC_SF_UNKNOWN_PL_LENGTH;
+                       htx_remove_blk(htx, blk);
+                       total += bsize;
+                       count -= bsize;
+                       break;
+
                case HTX_BLK_TLR:
                case HTX_BLK_EOT:
                default:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/mux_fcgi.c 
new/haproxy-3.4.1+git0.3e888a769/src/mux_fcgi.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/mux_fcgi.c    2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/mux_fcgi.c     2026-06-25 
15:54:08.000000000 +0200
@@ -4242,11 +4242,14 @@
 
   done:
        if (fstrm->state >= FCGI_SS_HLOC) {
+               struct htx_ret htxret;
+
                /* trim any possibly pending data after we close (extra CR-LF,
                 * unprocessed trailers, abnormal extra data, ...)
                 */
-               total += count;
-               count = 0;
+               htxret = htx_drain(htx, count);
+               total += htxret.ret;
+               count -= htxret.ret;
        }
 
        if (fstrm->state == FCGI_SS_ERROR) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/mux_h2.c 
new/haproxy-3.4.1+git0.3e888a769/src/mux_h2.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/mux_h2.c      2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/mux_h2.c       2026-06-25 
15:54:08.000000000 +0200
@@ -8186,11 +8186,14 @@
 
   done:
        if (h2s->st >= H2_SS_HLOC) {
+               struct htx_ret htxret;
+
                /* trim any possibly pending data after we close (extra CR-LF,
                 * unprocessed trailers, abnormal extra data, ...)
                 */
-               total += count;
-               count = 0;
+               htxret = htx_drain(htx, count);
+               total += htxret.ret;
+               count -= htxret.ret;
        }
 
        /* RST are sent similarly to frame acks */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/mux_spop.c 
new/haproxy-3.4.1+git0.3e888a769/src/mux_spop.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/mux_spop.c    2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/mux_spop.c     2026-06-25 
15:54:08.000000000 +0200
@@ -3427,6 +3427,7 @@
 
        if (spop_strm->state >= SPOP_SS_HLOC) {
                /* trim any possibly pending data after we close */
+               b_del(buf, count);
                total += count;
                count = 0;
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-3.4.0+git31.fc300e9f2/src/qcm_http.c 
new/haproxy-3.4.1+git0.3e888a769/src/qcm_http.c
--- old/haproxy-3.4.0+git31.fc300e9f2/src/qcm_http.c    2026-06-18 
09:19:41.000000000 +0200
+++ new/haproxy-3.4.1+git0.3e888a769/src/qcm_http.c     2026-06-25 
15:54:08.000000000 +0200
@@ -111,14 +111,15 @@
 size_t qcs_http_reset_buf(struct qcs *qcs, struct buffer *buf, size_t count)
 {
        struct htx *htx;
+       struct htx_ret htxret;
 
        TRACE_ENTER(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
 
        htx = htx_from_buf(buf);
-       htx_reset(htx);
+       htxret = htx_drain(htx, count);
        htx_to_buf(htx, buf);
 
        TRACE_LEAVE(QMUX_EV_STRM_SEND, qcs->qcc->conn, qcs);
 
-       return count;
+       return htxret.ret;
 }

++++++ haproxy-opentelemetry-ce86d09.tar.gz -> 
haproxy-opentelemetry-6344dfa.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/README 
new/haproxy-opentelemetry-6344dfa/README
--- old/haproxy-opentelemetry-ce86d09/README    2026-06-22 02:54:04.000000000 
+0200
+++ new/haproxy-opentelemetry-6344dfa/README    2026-06-24 22:15:31.000000000 
+0200
@@ -373,7 +373,8 @@
 
 groups <name> ...
   A list of "otel-group" groups used for the currently defined instrumentation
-  is declared.  Several groups can be specified in one line.
+  is declared.  Several groups can be specified in one line, and the keyword 
may
+  be repeated on multiple lines.
 
   Arguments :
     name - the name of the OTel group
@@ -426,9 +427,9 @@
 
 
 scopes <name> ...
-  This keyword declares a list of "otel-scope" definitions used for the
-  currently defined instrumentation.  Multiple scopes can be specified in the
-  same line.
+  This keyword lists the "otel-scope" definitions used by the currently defined
+  instrumentation.  Multiple scopes may be given on one line, and the keyword
+  may be repeated on multiple lines.
 
   Arguments :
     name - the name of the OTel scope
@@ -456,6 +457,7 @@
     - attribute
     - baggage
     - event
+    - exception
     - extract
     - finish
     - idle-timeout
@@ -625,6 +627,11 @@
   transfer via HTTP header can be used to transfer data to another process (ie
   microservice).  All data is organized in the form of key-value data pairs.
 
+  When neither 'use-vars' nor 'use-headers' is given, injection defaults to
+  'use-headers'.  A span carries a single context, so 'inject' may appear at
+  most once per span; a second 'inject' on the same span is a configuration
+  error.
+
   Header injection (use-headers) is only available on events that provide a
   writable HTTP header carrier when they fire.  Using it on any other event is
   a configuration error, reported at startup.  It is rejected for these events:
@@ -642,7 +649,9 @@
   can be used to construct the data name prefix.  Uppercase letters can 
actually
   be used, but they will be converted to lowercase when creating the prefix.
   The special prefix '-' can be used to generate the name automatically from 
the
-  scope's event name or the span name.
+  scope's event name or the span name.  The generated name keeps a leading '-',
+  so by the rule below the injected headers carry only the bare propagation
+  headers, with no HAProxy-specific name prefix.
 
   When the OpenTelemetry context is transferred via the HTTP header and the
   first character of the context name is '-', the context name is excluded
@@ -681,26 +690,26 @@
   'finish' keyword or when detaching the stream from the filter.
 
 
-instrument { update <name> [<attr> ...] | <type> <name> [<aggr>] [<desc>] 
[<unit>] <value> [<bounds>] } [{ if | unless } <condition>]
+instrument <type> <name> [aggr <aggregation>] [desc <description>] [unit 
<unit>] value <sample> [bounds <bounds>] [{ if | unless } <condition>]
+instrument update <name> [attr <key> <sample> ...] [{ if | unless } 
<condition>]
   This keyword allows creating or updating metric instruments within the scope.
   Metric instruments record numerical measurements that are exported alongside
   traces.
 
-  To create a new instrument, specify the instrument type, a name, and a sample
-  expression providing the measurement value (preceded by the 'value' keyword).
-  Optionally, a human-readable description (preceded by 'desc') and a unit
-  string (preceded by 'unit') can be added.
+  To create a new instrument, give the instrument type, a name, and a sample
+  expression for the measurement value, preceded by the 'value' keyword.  An
+  optional description ('desc') and unit string ('unit') may also be given.  
The
+  'aggr', 'desc', 'unit', 'value' and 'bounds' keywords may appear in any 
order.
 
   An aggregation type can be specified using the 'aggr' keyword followed by one
   of the supported aggregation types listed below.  When specified, a metrics
   view is registered with the given aggregation strategy.  If no aggregation
   type is specified, the SDK default is used.
 
-  For histogram instruments (hist_int), optional bucket boundaries can be
-  specified using the 'bounds' keyword followed by a double-quoted string of
-  space-separated integers in strictly ascending order.  When bounds are
-  specified without an explicit aggregation type, histogram aggregation is
-  used automatically.
+  For histogram instruments (hist_int), optional bucket boundaries follow the
+  'bounds' keyword as a double-quoted string of space-separated numbers, which
+  are sorted internally; any duplicate boundaries are then rejected.  With no
+  explicit aggregation type set, histogram aggregation is applied 
automatically.
 
   To update an existing instrument (previously created in another scope), use
   'update' followed by the name of the instrument.  Optional attributes can be
@@ -753,10 +762,16 @@
   form it gates every recording of the instrument, on an update form only that
   one.  Registration itself produces no data point and is never gated.
 
+  The update form supplies no value of its own, only attributes and an optional
+  condition; to record a different quantity, define a separate instrument.  To
+  feed a computed or externally-set value, set the create form's 'value' to a
+  'var(...)' fetch and assign that variable with 'set-var'.
+
   For example:
     instrument cnt_int  "my_counter" desc "Counter" value int(1)
     instrument hist_int "my_hist" aggr exp_histogram desc "Latency" value 
lat_ns_tot unit "ns"
     instrument hist_int "my_hist2" desc "Latency" value lat_ns_tot unit "ns" 
bounds "100 1000 10000 100000"
+    instrument hist_int "req_bytes" value var(txn.otel_size) unit "By"
     instrument update "my_counter" attr "key1" str("val1")
 
   Arguments :
@@ -805,8 +820,9 @@
     error, error2, error3, error4, fatal, fatal2, fatal3, fatal4
 
   The log record is only emitted when the logger is enabled for the configured
-  severity.  If a 'span' reference is given but the named span is not found at
-  runtime, the log record is emitted without span correlation.
+  severity (controlled by the 'min_severity' option in the YAML logs signal
+  configuration).  If a 'span' reference is given but the named span is not
+  found at runtime, the log record is emitted without span correlation.
 
   An optional trailing 'if'/'unless' condition (same syntax as 'otel-event')
   gates the whole record; when it does not pass at runtime, the record is
@@ -850,6 +866,9 @@
   value; see the 'attribute' keyword description for the data type conversion
   table.
 
+  Because the 'attr' keyword directly after the first name selects the 
attribute
+  form, a link target in that position cannot itself be the literal word 
'attr'.
+
   For example:
     link "Span A" "Span B"
     link "inbound" attr "link.detail" str("follows-from")
@@ -1206,6 +1225,11 @@
   marks the span as a root span.  The 'kind' keyword sets the OTel span kind,
   one of internal, server (the default), client, producer or consumer.
 
+  Each 'span' opens a sub-context within the scope: the 'attribute', 'event',
+  'baggage', 'status', 'link' and 'inject' keywords that follow it apply to 
that
+  span -- the 'currently active span' -- until the next 'span' or the end of 
the
+  scope.
+
   For example:
     span "HAProxy session" root
     span "Client session" parent "HAProxy session"
@@ -1382,11 +1406,10 @@
 4.4. "otel-group" section
 --------------------------
 
-This section allows us to define a group of OTel scopes, that is not activated
-via an event but is triggered from TCP or HTTP rules.  More precisely, these 
are
-the following rules: 'tcp-request', 'tcp-response', 'http-request',
-'http-response' and 'http-after-response'.  These rules can be defined in the
-HAProxy configuration file.
+This section defines a group of OTel scopes that is not triggered by an event
+but by HAProxy rules in the configuration file -- specifically 'http-request',
+'http-response', 'http-after-response', 'tcp-request content' and 'tcp-response
+content'.
 
 The action keyword used in these rules is 'otel-group', and it takes the filter
 id and the group name as arguments:
@@ -1429,6 +1452,18 @@
   Arguments :
     name - the name of the span context to check
 
+  The filter also exposes the raw payload byte counts accumulated on the 
stream:
+
+  otel.bytes_in  : integer
+  otel.bytes_out : integer
+
+  'otel.bytes_in' counts the payload on the request channel (client to HAProxy)
+  and 'otel.bytes_out' the payload on the response channel (HAProxy to client).
+  Both grow as data is forwarded, so they are best read from a close scope --
+  on-client-session-end or on-server-session-end -- where they hold the final
+  connection totals.  They are populated in TCP mode; on an HTTP proxy they 
read
+  0, because an HTX buffer carries protocol framing rather than raw payload.
+
 
 5. Examples
 ------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/README-configuration 
new/haproxy-opentelemetry-6344dfa/README-configuration
--- old/haproxy-opentelemetry-ce86d09/README-configuration      2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/README-configuration      2026-06-24 
22:15:31.000000000 +0200
@@ -243,8 +243,9 @@
         name.  A reference that resolves to no span or extracted context --
         for instance a 'parent' on a context whose extraction found nothing --
         is an error, and the span is not created.
-      - 'link <ref>' adds an inline link to another span or context.  Multiple
-        inline links can be specified within the argument limit.
+      - 'link <ref>' adds an inline link to another span or context.  Only one
+        inline link fits the span's argument limit; use the standalone 'link'
+        keyword to add more.
       - 'kind <kind>' sets the span kind to one of the following: 'internal',
         'server' (default), 'client', 'producer' or 'consumer'.
       - If no reference is given, the span becomes a root span.
@@ -370,8 +371,10 @@
   inject <name-prefix> [use-vars] [use-headers]
       Inject span context into an HTTP header carrier and/or HAProxy variables.
       The prefix names the context; the special prefix '-' generates the name
-      automatically.  Default storage: use-headers.  The 'use-vars' option
-      requires OTEL_USE_VARS=1 at compile time.
+      automatically.  An autogenerated name keeps a leading '-', so the 
injected
+      headers carry only the bare propagation headers (see below).  Default
+      storage: use-headers.  The 'use-vars' option requires OTEL_USE_VARS=1 at
+      compile time.
 
       A span carries a single context, so 'inject' may appear at most once per
       span; a second is a configuration error.
@@ -478,6 +481,21 @@
       form only that one.  Registration produces no data point and is never
       gated.
 
+      The update form itself supplies no value, only attributes and an optional
+      condition; to record a different quantity, define a separate instrument.
+      To feed a computed or externally-set measurement into one instrument, set
+      the create form's 'value' to a 'var(...)' fetch and assign that variable
+      with 'set-var'; the instrument then records whatever the variable holds.
+
+      For example, a request size read from a header is stored in a variable
+      with 'set-var', then recorded through it:
+
+        otel-scope request_size
+            set-var txn.otel_size req.hdr(content-length)
+            instrument hist_int req_bytes value var(txn.otel_size) unit "By"
+            instrument update req_bytes
+            otel-event on-frontend-http-request
+
       Observable (pull) instruments are unsupported, but the create/update 
split
       covers the same need when the update form runs periodically.  Bind a 
scope
       to the 'on-idle-timeout' event, whose timer fires on a HAProxy thread at
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/README-design 
new/haproxy-opentelemetry-6344dfa/README-design
--- old/haproxy-opentelemetry-ce86d09/README-design     2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/README-design     2026-06-24 
22:15:31.000000000 +0200
@@ -296,9 +296,10 @@
   PENDING (-2)  Creation in progress by another thread.
   >= 0          Valid meter index.  Ready for recording.
 
-Threads that lose the CAS skip creation.  Update-form instruments check that 
the
-referenced create-form's idx is >= 0 before recording; if it is still PENDING 
or
-UNSET, the measurement is silently skipped.
+Threads that lose the CAS skip creation.  Update-form instruments check the
+referenced create-form's idx before recording: an idx that is still PENDING is
+waited for until the creating thread resolves it, then recorded; an idx of 
UNSET
+(never created, or a creation that failed) is skipped.
 
 4.3  Per-Thread Initialization Guard
 
@@ -371,9 +372,10 @@
   SMP_T_METH     lookup from static HTTP method table, or raw string for
                  HTTP_METH_OTHER
 
-For metric instruments, string values are rejected -- the meter requires
-OTELC_VALUE_INT64.  If the sample evaluates to a string, otelc_value_strtonum()
-attempts numeric conversion as a last resort.
+For metric instruments the value is always int64.  A string value -- for 
example
+a value read from a variable, which the filter stores as a string -- is coerced
+with otelc_value_strtonum(); only a value that is not a valid integer is
+rejected.
 
 5.3  Dispatch to Data Structures
 
@@ -432,14 +434,14 @@
 6.2  ACL-Based Filtering
 
 Each scope may carry an ACL condition (if/unless) evaluated at scope
-execution time via acl_exec_cond().  ACL references are resolved through
-a priority chain in flt_otel_parse_acl() (parser.c):
+execution time via acl_exec_cond().  flt_otel_parse_acl() (parser.c) resolves
+an ACL reference by trying the lists its callers supply, in order, until
+build_acl_cond() succeeds.  The callers pass the lists in this priority order:
 
   1. Scope-local ACLs (declared within the otel-scope section).
   2. Instrumentation ACLs (declared in the otel-instrumentation section).
   3. Proxy ACLs (declared in the HAProxy frontend/backend section).
 
-The first list that produces a successful build_acl_cond() result is used.
 If the condition fails at runtime:
 
   - If the scope contains a root span, the entire stream is disabled
@@ -485,9 +487,8 @@
                                   callback).
 
 Pool registration is conditional on compile-time flags (USE_POOL_OTEL_* in
-config.h).  flt_otel_pool_alloc() attempts pool allocation first and falls back
-to heap allocation (OTELC_MALLOC) when the pool is NULL or the requested size
-exceeds the pool's element size.
+config.h).  flt_otel_pool_alloc() allocates from the pool when one is present,
+and from the heap (OTELC_MALLOC) when the pool is NULL.
 
 The C wrapper library's memory allocations are redirected through
 flt_otel_mem_malloc() / flt_otel_mem_free() (filter.c), which use the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/README-func 
new/haproxy-opentelemetry-6344dfa/README-func
--- old/haproxy-opentelemetry-ce86d09/README-func       2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/README-func       2026-06-24 
22:15:31.000000000 +0200
@@ -163,10 +163,13 @@
       first lazily creates create-form instruments via the meter, using
       HA_ATOMIC_CAS to guarantee thread-safe one-time initialization; the 
second
       iterates update-form instruments and records measurements via
-      flt_otel_scope_run_instrument_record().  Instruments whose index is still
-      negative (UNUSED or PENDING) are skipped.  A measurement is recorded only
-      when the create-form and update-form if/unless conditions pass; 
instrument
-      creation produces no data point and is never gated.
+      flt_otel_scope_run_instrument_record().  An instrument another thread is
+      creating right now (PENDING index) is waited for until that creation
+      completes rather than skipped, so a concurrent first use does not lose 
its
+      measurement; one not created at all (UNSET index) is skipped.  A
+      measurement is recorded only when the create-form and update-form
+      if/unless conditions pass; instrument creation produces no data point and
+      is never gated.
 
   flt_otel_scope_run_log_record
       Emits log records for a scope.  Iterates over the configured log-record
@@ -273,9 +276,9 @@
       letter, which the generated HAProxy variable normalizes.
 
   flt_otel_parse_cfg_check
-      Common validation for config keywords: looks up the keyword, checks
-      argument count and character validity, verifies that the parent section 
ID
-      is set.
+      Common validation for config keywords: looks up the keyword, checks the
+      argument count and character validity, and requires the instrumentation 
to
+      be defined when the section calls for it.
 
   flt_otel_parse_cfg_sample_expr
       Parses a single HAProxy sample expression within a sample definition.
@@ -305,6 +308,10 @@
       Checks whether the current config parsing is within the correct HAProxy
       configuration scope (cfg_scope filtering).
 
+  flt_otel_parse_cfg_acl
+      Parses the 'acl' keyword shared by the otel-instrumentation and 
otel-scope
+      sections, rejecting 'or' as a name before calling parse_acl().
+
   flt_otel_parse_cfg_instr
       Section parser for the otel-instrumentation block.  Handles keywords:
       otel-instrumentation ID, log, config, groups, scopes, acl, rate-limit,
@@ -322,14 +329,22 @@
       Post-parse callback for otel-group.  Checks that at least one scope is
       defined.
 
+  flt_otel_parse_ctx_flag
+      Recognizes a context storage type token ("use-headers" or "use-vars") and
+      returns the matching flag; shared by the inject and extract keywords.
+
   flt_otel_parse_cfg_scope_ctx
       Parses the context storage type argument ("use-headers" or "use-vars") 
for
-      inject/extract keywords.
+      the inject keyword, recording it on the current span.
 
   flt_otel_parse_acl
       Builds an ACL condition by trying multiple ACL lists in order
       (scope-local, instrumentation, proxy).
 
+  flt_otel_parse_trailing_cond
+      Builds a mandatory trailing 'if'/'unless' condition; used by exception,
+      set-var-ctx, otel-stop and otel-event.
+
   flt_otel_parse_bounds
       Parses a space-separated string of numbers into a dynamically allocated
       array of doubles for histogram bucket boundaries.  Sorts the values
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/README-implementation 
new/haproxy-opentelemetry-6344dfa/README-implementation
--- old/haproxy-opentelemetry-ce86d09/README-implementation     2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/README-implementation     2026-06-24 
22:15:31.000000000 +0200
@@ -461,8 +461,8 @@
     6. Processes metric instruments via flt_otel_scope_run_instrument(), which
        runs two passes: the first lazily creates create-form instruments using
        HA_ATOMIC_CAS for thread-safe one-time initialization; the second 
records
-       measurements for update-form instruments, skipping any whose index is
-       still negative (creation pending or not yet attempted).
+       measurements for update-form instruments, waiting for any still being
+       created (PENDING) and skipping any never created (UNSET).
     7. Emits log records via flt_otel_scope_run_log_record(), which iterates
        the scope's log-record list, skips entries below the logger's severity
        threshold, evaluates sample expressions into a body string, resolves
@@ -708,8 +708,7 @@
   pool.c provides wrappers around HAProxy memory pools and standard
   allocation:
 
-  flt_otel_pool_alloc()   Allocates from a pool (if non-NULL and the requested
-                          size fits) or via calloc.
+  flt_otel_pool_alloc()   Allocates from a pool (when non-NULL) or the heap.
   flt_otel_pool_free()    Returns memory to the pool or frees it.
   flt_otel_pool_strndup() Duplicates a string via pool allocation.
   flt_otel_trash_alloc()  Acquires a trash buffer chunk.
@@ -1109,6 +1108,32 @@
 define the instrument, and "update" form instruments record measurements
 against it.
 
+The OpenTelemetry data model itself works this way: an instrument is created
+once with a fixed identity, and measurements are recorded against it 
repeatedly.
+The create form is that one-time creation; the update form is the recording.
+What the create form pins is not only the identity -- name, description, unit,
+kind, aggregation and any histogram bounds -- but also the sample expression
+that produces the value.  The update form has no value expression of its own; 
it
+contributes only the recording attributes, an optional if/unless condition and,
+through the scope that it lives in, the event that drives the recording.
+
+The expression is fixed, the recorded number is not.  Each update re-evaluates
+the create form's value expression against the current stream, so a live fetch
+such as fe_conn or lat_ns_tot yields a fresh reading on every run, whereas a
+constant such as int(1) yields a steady increment.  That value is then applied
+through the instrument's native operation: an additive delta for counters and
+up-down counters, a distribution sample for histograms, and a last-value 
reading
+for gauges.  A fixed expression is therefore a valid source for the model's
+recording side.
+
+A create form emits no data point on its own; a measurement appears only when 
an
+update form for that instrument runs.  Within a scope a name may carry at most
+one create form and one update form, but update forms in other scopes may 
target
+the same create form, so one instrument can be recorded from several sites, 
each
+with its own attributes, condition and driving event.  Across those sites the
+one thing that cannot vary is the value expression; to record another quantity,
+declare a separate instrument instead.
+
 18.5.1  Instrument Types
 
 The following instrument types are available (parser.h):
@@ -1194,8 +1219,10 @@
     a. Reference validation: the ref pointer must be non-NULL (resolved
        at check time to the create-form instrument).
 
-    b. Index check: if the create-form instrument's idx is still
-       negative (UNUSED or PENDING), the measurement is skipped.
+    b. Index check: if the create-form instrument's idx is PENDING, the
+       thread waits with ha_thread_relax() until the creating thread
+       resolves it; an idx that is (or becomes) UNSET means creation
+       never happened or failed, and the measurement is skipped.
 
     c. Recording: flt_otel_scope_run_instrument_record() evaluates the
        sample expression, converts it to an otelc_value, and calls
@@ -1214,13 +1241,13 @@
   buffer of global.tune.bufsize, calls build_logline() to evaluate the
   log-format expression, and presents the result as an SMP_T_STR sample.
 
-Both paths converge on flt_otel_sample_to_value(), which converts the
-HAProxy sample data to an otelc_value.  Metric instruments require
-numeric values (INT64); if the conversion produces OTELC_VALUE_DATA
-(string), a warning is logged and the value is then run through
-otelc_value_strtonum() with OTELC_VALUE_INT64.  A numeric string is
-coerced and recorded; the measurement is rejected only when that
-conversion fails.
+Both paths converge on flt_otel_sample_to_value(), which converts the HAProxy
+sample data to an otelc_value.  A metric instrument value is always int64, so
+a string result (for example a value read from a variable, which the filter
+stores as a string) is coerced through otelc_value_strtonum() with
+OTELC_VALUE_INT64.  A numeric string is coerced and recorded; the measurement
+is rejected, with a warning that names the value, only when that conversion
+fails.
 
 
 18.5.6  Instrument Lifecycle Summary
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/include/filter.h 
new/haproxy-opentelemetry-6344dfa/include/filter.h
--- old/haproxy-opentelemetry-ce86d09/include/filter.h  2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/include/filter.h  2026-06-24 
22:15:31.000000000 +0200
@@ -45,7 +45,7 @@
 bool flt_otel_is_disabled(const struct filter *f FLT_OTEL_DBG_ARGS(, int 
event));
 
 /* Validate a scope's fetches and conditions against a processing point. */
-void flt_otel_check_scope_loc(const struct flt_otel_conf *conf, const struct 
flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, const char 
*trigger);
+int  flt_otel_check_scope_loc(const struct flt_otel_conf *conf, const struct 
flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, const char 
*trigger);
 
 #endif /* _OTEL_FILTER_H_ */
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/include/parser.h 
new/haproxy-opentelemetry-6344dfa/include/parser.h
--- old/haproxy-opentelemetry-ce86d09/include/parser.h  2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/include/parser.h  2026-06-24 
22:15:31.000000000 +0200
@@ -193,7 +193,7 @@
 /* Keyword metadata used by the configuration section parsers. */
 struct flt_otel_parse_data {
        int         keyword;       /* Keyword index. */
-       bool        flag_check_id; /* Whether the group ID must be defined for 
the keyword. */
+       bool        flag_check_id; /* Whether a span must be open for the 
keyword. */
        int         check_name;    /* Checking allowed characters in the name. 
*/
        int         args_min;      /* The minimum number of arguments required. 
*/
        int         args_max;      /* The maximum number of arguments allowed. 
*/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/include/pool.h 
new/haproxy-opentelemetry-6344dfa/include/pool.h
--- old/haproxy-opentelemetry-ce86d09/include/pool.h    2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/include/pool.h    2026-06-24 
22:15:31.000000000 +0200
@@ -19,7 +19,7 @@
                if ((p) != NULL) {                                     \
                        OTELC_DBG(DEBUG, #p " %p %u", (p), (p)->size); \
                                                                       \
-                       pool_destroy(p);                               \
+                       (void)pool_destroy(p);                         \
                        (p) = NULL;                                    \
                }                                                      \
        } while (0)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/src/conf.c 
new/haproxy-opentelemetry-6344dfa/src/conf.c
--- old/haproxy-opentelemetry-ce86d09/src/conf.c        2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/src/conf.c        2026-06-24 
22:15:31.000000000 +0200
@@ -872,7 +872,7 @@
        FLT_OTEL_DBG_CONF_SCOPE("- conf_scope free ", *ptr);
 
        list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) {
-               prune_acl(acl);
+               (void)prune_acl(acl);
                FLT_OTEL_LIST_DEL(&(acl->list));
                OTELC_SFREE(acl);
        }
@@ -1000,7 +1000,7 @@
        OTELC_SFREE((*ptr)->config);
        OTELC_DBG(DEBUG, "- deleting acls list %s", 
flt_otel_list_dump(&((*ptr)->acls)));
        list_for_each_entry_safe(acl, aclback, &((*ptr)->acls), list) {
-               prune_acl(acl);
+               (void)prune_acl(acl);
                FLT_OTEL_LIST_DEL(&(acl->list));
                OTELC_SFREE(acl);
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/src/event.c 
new/haproxy-opentelemetry-6344dfa/src/event.c
--- old/haproxy-opentelemetry-ce86d09/src/event.c       2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/src/event.c       2026-06-24 
22:15:31.000000000 +0200
@@ -66,7 +66,9 @@
  *   the resulting value via the <meter> API.  Each expression is evaluated
  *   with sample_process(), converted to an otelc_value via
  *   flt_otel_sample_to_value(), and recorded via
- *   <meter>->update_instrument_kv_n().
+ *   <meter>->update_instrument_kv_n().  An instrument value is always
+ *   integer-typed, so a string value is coerced to int64 and rejected only
+ *   when it is not a valid integer.
  *
  * RETURN VALUE
  *   Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
@@ -151,18 +153,16 @@
                OTELC_DBG_VALUE(DEBUG, "value ", &value);
 
                /*
-                * Metric instruments expect numeric values (INT64 or DOUBLE).
-                * Reject OTELC_VALUE_DATA since the meter cannot interpret
-                * arbitrary string data as a numeric measurement.
+                * A metric instrument value is always integer-typed.  A
+                * string value (such as a var() fetch) is coerced to int64;
+                * a value that is not a valid integer is rejected.
                 */
-               if (value.u_type == OTELC_VALUE_DATA) {
-                       OTELC_DBG(WARNING, "WARNING: non-numeric value type for 
instrument '%s'", instr_ref->id);
+               if ((value.u_type == OTELC_VALUE_DATA) && 
(otelc_value_strtonum(&value, OTELC_VALUE_INT64) == OTELC_RET_ERROR)) {
+                       OTELC_DBG(WARNING, "WARNING: non-numeric value '%s' for 
instrument '%s'", (const char *)(value.u.value_data), instr_ref->id);
 
-                       if (otelc_value_strtonum(&value, OTELC_VALUE_INT64) == 
OTELC_RET_ERROR) {
-                               OTELC_SFREE(value.u.value_data);
+                       OTELC_SFREE(value.u.value_data);
 
-                               retval = FLT_OTEL_RET_ERROR;
-                       }
+                       retval = FLT_OTEL_RET_ERROR;
                }
 
                if (retval != FLT_OTEL_RET_ERROR)
@@ -199,11 +199,13 @@
  *   on first use, using HA_ATOMIC_CAS on the instrument index to guarantee
  *   thread-safe one-time initialization.  The second pass iterates over
  *   update-form instruments and records measurements via
- *   flt_otel_scope_run_instrument_record().  Instruments whose index is still
- *   negative (UNUSED or PENDING) are skipped, so that a concurrent creation by
- *   another thread does not cause an invalid <meter> access.  A measurement
- *   is recorded only when both the create-form and update-form 'if'/'unless'
- *   conditions pass; creation itself produces no data point and is never 
gated.
+ *   flt_otel_scope_run_instrument_record().  An instrument another thread is
+ *   creating right now (PENDING index) is waited for until that creation
+ *   completes rather than skipped, so a concurrent first use does not lose
+ *   its measurement; one not created at all (UNSET index) is skipped.  A
+ *   measurement is recorded only when both the create-form and update-form
+ *   'if'/'unless' conditions pass; creation itself produces no data point and
+ *   is never gated.
  *
  * RETURN VALUE
  *   Returns FLT_OTEL_RET_OK on success, FLT_OTEL_RET_ERROR on failure.
@@ -266,6 +268,17 @@
                        FLT_OTEL_DBG_CONF_INSTRUMENT("", conf_instr);
 
                        /*
+                        * If another thread is creating this instrument
+                        * (PENDING index), yield until it finishes rather than
+                        * dropping the measurement.  The creating thread always
+                        * resolves PENDING (stores the index, or UNSET on
+                        * failure), so this ends as soon as creation does.
+                        */
+                       if (instr != NULL)
+                               while (HA_ATOMIC_LOAD(&(instr->idx)) == 
OTELC_METRIC_INSTRUMENT_PENDING)
+                                       ha_thread_relax();
+
+                       /*
                         * Update form: record a measurement using an existing
                         * create-form instrument.
                         */
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/src/filter.c 
new/haproxy-opentelemetry-6344dfa/src/filter.c
--- old/haproxy-opentelemetry-ce86d09/src/filter.c      2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/src/filter.c      2026-06-24 
22:15:31.000000000 +0200
@@ -508,7 +508,7 @@
  *   flt_otel_check_cond_loc - per-scope ACL condition location check
  *
  * SYNOPSIS
- *   static void flt_otel_check_cond_loc(const struct flt_otel_conf *conf, 
const struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint 
where, const struct acl_cond *cond, const char *kind, const char *trigger)
+ *   static int flt_otel_check_cond_loc(const struct flt_otel_conf *conf, 
const struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint 
where, const struct acl_cond *cond, const char *kind, const char *trigger)
  *
  * ARGUMENTS
  *   conf       - the OTel filter configuration
@@ -526,24 +526,25 @@
  *   forwarded as a filter warning.  A NULL condition is ignored.
  *
  * RETURN VALUE
- *   This function does not return a value.
+ *   Returns ERR_WARN if the condition can never match at <where>, otherwise 0.
  */
-static void flt_otel_check_cond_loc(const struct flt_otel_conf *conf, const 
struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, 
const struct acl_cond *cond, const char *kind, const char *trigger)
+static int flt_otel_check_cond_loc(const struct flt_otel_conf *conf, const 
struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, 
const struct acl_cond *cond, const char *kind, const char *trigger)
 {
        char *err = NULL;
+       int   retval = 0;
 
        OTELC_FUNC("%p, %p, %p, 0x%08x, %p, \"%s\", \"%s\"", conf, conf_scope, 
p, where, cond, OTELC_STR_ARG(kind), OTELC_STR_ARG(trigger));
 
        if (cond == NULL)
-               OTELC_RETURN();
+               OTELC_RETURN_INT(retval);
 
-       (void)warnif_cond_conflicts(cond, where, &err);
+       retval = warnif_cond_conflicts(cond, where, &err);
        if (err != NULL)
                FLT_OTEL_WARNING("''%s' : " FLT_OTEL_PARSE_SECTION_SCOPE_ID " 
'%s' %s condition at %s on proxy '%s': %s'", conf->id, conf_scope->id, kind, 
trigger, p->id, err);
 
        ha_free(&err);
 
-       OTELC_RETURN();
+       OTELC_RETURN_INT(retval);
 }
 
 
@@ -552,7 +553,7 @@
  *   flt_otel_check_sample_list - per-scope sample-fetch location check
  *
  * SYNOPSIS
- *   static void flt_otel_check_sample_list(const struct flt_otel_conf *conf, 
const struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint 
where, const struct list *head, const char *kind, const char *trigger)
+ *   static int flt_otel_check_sample_list(const struct flt_otel_conf *conf, 
const struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint 
where, const struct list *head, const char *kind, const char *trigger)
  *
  * ARGUMENTS
  *   conf       - the OTel filter configuration
@@ -573,18 +574,20 @@
  *   log-format expressions (parsed at SMP_VAL_FE_LOG_END) are left unchecked.
  *
  * RETURN VALUE
- *   This function does not return a value.
+ *   Returns ERR_WARN if a sample's condition or fetch cannot be evaluated at
+ *   <where>, otherwise 0.
  */
-static void flt_otel_check_sample_list(const struct flt_otel_conf *conf, const 
struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, 
const struct list *head, const char *kind, const char *trigger)
+static int flt_otel_check_sample_list(const struct flt_otel_conf *conf, const 
struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, 
const struct list *head, const char *kind, const char *trigger)
 {
        const struct flt_otel_conf_sample      *sample;
        const struct flt_otel_conf_sample_expr *conf_expr;
+       int                                     retval = 0;
 
        OTELC_FUNC("%p, %p, %p, 0x%08x, %p, \"%s\", \"%s\"", conf, conf_scope, 
p, where, head, OTELC_STR_ARG(kind), OTELC_STR_ARG(trigger));
 
        list_for_each_entry(sample, head, list) {
                /* The optional 'if'/'unless' condition on this directive. */
-               flt_otel_check_cond_loc(conf, conf_scope, p, where, 
sample->cond, kind, trigger);
+               retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
sample->cond, kind, trigger);
 
                /*
                 * Log-format expressions are parsed at SMP_VAL_FE_LOG_END,
@@ -609,10 +612,12 @@
                                continue;
 
                        FLT_OTEL_WARNING("''%s' : " 
FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s' %s fetch '%s' extracts from '%s', not 
usable at %s on proxy '%s''", conf->id, conf_scope->id, kind, 
conf_expr->expr->fetch->kw, sample_src_names(conf_expr->expr->fetch->use), 
trigger, p->id);
+
+                       retval |= ERR_WARN;
                }
        }
 
-       OTELC_RETURN();
+       OTELC_RETURN_INT(retval);
 }
 
 
@@ -621,7 +626,7 @@
  *   flt_otel_check_scope_loc - validate a scope's fetches at a processing 
point
  *
  * SYNOPSIS
- *   void flt_otel_check_scope_loc(const struct flt_otel_conf *conf, const 
struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, 
const char *trigger)
+ *   int flt_otel_check_scope_loc(const struct flt_otel_conf *conf, const 
struct flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, 
const char *trigger)
  *
  * ARGUMENTS
  *   conf       - the OTel filter configuration
@@ -640,9 +645,10 @@
  *   location) is a no-op.  Shared by the event-bound and group action checks.
  *
  * RETURN VALUE
- *   This function does not return a value.
+ *   Returns ERR_WARN if any of the scope's conditions or fetches cannot be
+ *   evaluated at <where>, otherwise 0.
  */
-void flt_otel_check_scope_loc(const struct flt_otel_conf *conf, const struct 
flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, const char 
*trigger)
+int flt_otel_check_scope_loc(const struct flt_otel_conf *conf, const struct 
flt_otel_conf_scope *conf_scope, const struct proxy *p, uint where, const char 
*trigger)
 {
        const struct flt_otel_conf_span        *conf_span;
        const struct flt_otel_conf_instrument  *conf_instrument;
@@ -651,46 +657,47 @@
        const struct flt_otel_conf_exception   *conf_exception;
        const struct flt_otel_conf_set_var_ctx *conf_set_var_ctx;
        const struct flt_otel_conf_unset_var   *conf_unset_var;
+       int                                     retval = 0;
 
        OTELC_FUNC("%p, %p, %p, 0x%08x, \"%s\"", conf, conf_scope, p, where, 
OTELC_STR_ARG(trigger));
 
        if (where == 0)
-               OTELC_RETURN();
+               OTELC_RETURN_INT(retval);
 
-       flt_otel_check_cond_loc(conf, conf_scope, p, where, conf_scope->cond, 
"otel-event", trigger);
-       flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_scope->stop_cond, "otel-stop", trigger);
+       retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_scope->cond, "otel-event", trigger);
+       retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_scope->stop_cond, "otel-stop", trigger);
 
        list_for_each_entry(conf_span, &(conf_scope->spans), list) {
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_span->attributes), "attribute", trigger);
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_span->events), "event", trigger);
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_span->baggages), "baggage", trigger);
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_span->statuses), "status", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_span->attributes), "attribute", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_span->events), "event", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_span->baggages), "baggage", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_span->statuses), "status", trigger);
                list_for_each_entry(conf_link, &(conf_span->links), list)
-                       flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_link->attributes), "link attribute", trigger);
+                       retval |= flt_otel_check_sample_list(conf, conf_scope, 
p, where, &(conf_link->attributes), "link attribute", trigger);
                list_for_each_entry(conf_exception, &(conf_span->exceptions), 
list) {
-                       flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_exception->message), "exception message", trigger);
-                       flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_exception->attributes), "exception attribute", trigger);
-                       flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_exception->cond, "exception", trigger);
+                       retval |= flt_otel_check_sample_list(conf, conf_scope, 
p, where, &(conf_exception->message), "exception message", trigger);
+                       retval |= flt_otel_check_sample_list(conf, conf_scope, 
p, where, &(conf_exception->attributes), "exception attribute", trigger);
+                       retval |= flt_otel_check_cond_loc(conf, conf_scope, p, 
where, conf_exception->cond, "exception", trigger);
                }
        }
        list_for_each_entry(conf_instrument, &(conf_scope->instruments), list) {
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_instrument->samples), "instrument value", trigger);
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_instrument->attributes), "instrument attribute", trigger);
-               flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_instrument->cond, "instrument", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_instrument->samples), "instrument value", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_instrument->attributes), "instrument attribute", trigger);
+               retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_instrument->cond, "instrument", trigger);
        }
        list_for_each_entry(conf_log_record, &(conf_scope->log_records), list) {
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_log_record->time), "log-record time", trigger);
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_log_record->attributes), "log-record attribute", trigger);
-               flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_log_record->samples), "log-record body", trigger);
-               flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_log_record->cond, "log-record", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_log_record->time), "log-record time", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_log_record->attributes), "log-record attribute", trigger);
+               retval |= flt_otel_check_sample_list(conf, conf_scope, p, 
where, &(conf_log_record->samples), "log-record body", trigger);
+               retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_log_record->cond, "log-record", trigger);
        }
-       flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_scope->set_vars), "set-var", trigger);
+       retval |= flt_otel_check_sample_list(conf, conf_scope, p, where, 
&(conf_scope->set_vars), "set-var", trigger);
        list_for_each_entry(conf_set_var_ctx, &(conf_scope->set_var_ctxs), list)
-               flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_set_var_ctx->cond, "set-var-ctx", trigger);
+               retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_set_var_ctx->cond, "set-var-ctx", trigger);
        list_for_each_entry(conf_unset_var, &(conf_scope->unset_vars), list)
-               flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_unset_var->cond, "unset-var", trigger);
+               retval |= flt_otel_check_cond_loc(conf, conf_scope, p, where, 
conf_unset_var->cond, "unset-var", trigger);
 
-       OTELC_RETURN();
+       OTELC_RETURN_INT(retval);
 }
 
 
@@ -725,7 +732,7 @@
        struct flt_otel_conf_group *conf_group;
        struct flt_otel_conf_scope *conf_scope;
        struct flt_otel_conf_ph    *ph_group, *ph_scope;
-       int                         retval = 0, scope_unused_cnt = 0, 
span_root_cnt = 0;
+       int                         retval = 0, scope_unused_cnt = 0, 
span_root_cnt = 0, span_cnt = 0;
 
        OTELC_FUNC("%p, %p", p, fconf);
 
@@ -935,6 +942,7 @@
                        list_for_each_entry(conf_span, &(conf_scope->spans), 
list) {
                                FLT_OTEL_DBG_CONF_SPAN("   ", conf_span);
 
+                               span_cnt++;
                                span_root_cnt += conf_span->flag_root ? 1 : 0;
                        }
 
@@ -988,10 +996,10 @@
 
                                (void)snprintf(trigger, sizeof(trigger), "event 
'%s'", flt_otel_event_data[conf_scope->event].name);
 
-                               flt_otel_check_scope_loc(conf, conf_scope, p, 
where, trigger);
+                               (void)flt_otel_check_scope_loc(conf, 
conf_scope, p, where, trigger);
                        }
                } else {
-                       FLT_OTEL_ALERT("''%s' : unused " 
FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s''", conf->id, conf_scope->id);
+                       FLT_OTEL_WARNING("''%s' : unused " 
FLT_OTEL_PARSE_SECTION_SCOPE_ID " '%s''", conf->id, conf_scope->id);
 
                        scope_unused_cnt++;
                }
@@ -1003,14 +1011,14 @@
         * starting HAProxy.
         */
        if (scope_unused_cnt > 0)
-               FLT_OTEL_ALERT("''%s' : %d scope(s) not in use'", conf->id, 
scope_unused_cnt);
+               FLT_OTEL_WARNING("''%s' : %d scope(s) not in use'", conf->id, 
scope_unused_cnt);
 
-       if (LIST_ISEMPTY(&(conf->scopes)))
-               /* Do nothing. */;
+       if (span_cnt == 0)
+               /* No defined spans, so the root-span check does not apply. */;
        else if (span_root_cnt == 0)
-               FLT_OTEL_ALERT("''%s' : no span is marked as the root span'", 
conf->id);
+               FLT_OTEL_WARNING("''%s' : no span is marked as the root span'", 
conf->id);
        else if (span_root_cnt > 1)
-               FLT_OTEL_ALERT("''%s' : multiple spans are marked as the root 
span'", conf->id);
+               FLT_OTEL_WARNING("''%s' : multiple spans are marked as the root 
span'", conf->id);
 
        OTELC_DBG(DEBUG, "- defined instruments ----------");
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/src/group.c 
new/haproxy-opentelemetry-6344dfa/src/group.c
--- old/haproxy-opentelemetry-ce86d09/src/group.c       2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/src/group.c       2026-06-24 
22:15:31.000000000 +0200
@@ -125,7 +125,7 @@
  *   flt_otel_group_check_loc - validate a group's scopes at the action's point
  *
  * SYNOPSIS
- *   static void flt_otel_group_check_loc(const struct flt_otel_conf *conf, 
const char *group_id, const struct proxy *px, uint where)
+ *   static int flt_otel_group_check_loc(const struct flt_otel_conf *conf, 
const char *group_id, const struct proxy *px, uint where)
  *
  * ARGUMENTS
  *   conf     - the OTel filter configuration
@@ -140,14 +140,16 @@
  *   and passed to flt_otel_check_scope_loc().
  *
  * RETURN VALUE
- *   This function does not return a value.
+ *   Returns ERR_WARN if any of the group's scopes has a condition or fetch
+ *   that cannot be evaluated at <where>, otherwise 0.
  */
-static void flt_otel_group_check_loc(const struct flt_otel_conf *conf, const 
char *group_id, const struct proxy *px, uint where)
+static int flt_otel_group_check_loc(const struct flt_otel_conf *conf, const 
char *group_id, const struct proxy *px, uint where)
 {
        const struct flt_otel_conf_group *conf_group;
        const struct flt_otel_conf_ph    *ph_scope;
        const struct flt_otel_conf_scope *conf_scope;
        char                              trigger[160];
+       int                               retval = 0;
 
        OTELC_FUNC("%p, \"%s\", %p, 0x%08x", conf, OTELC_STR_ARG(group_id), px, 
where);
 
@@ -160,7 +162,7 @@
                list_for_each_entry(ph_scope, &(conf_group->ph_scopes), list)
                        list_for_each_entry(conf_scope, &(conf->scopes), list)
                                if (strcmp(conf_scope->id, ph_scope->id) == 0) {
-                                       flt_otel_check_scope_loc(conf, 
conf_scope, px, where, trigger);
+                                       retval |= 
flt_otel_check_scope_loc(conf, conf_scope, px, where, trigger);
 
                                        break;
                                }
@@ -168,7 +170,7 @@
                break;
        }
 
-       OTELC_RETURN();
+       OTELC_RETURN_INT(retval);
 }
 
 
@@ -281,7 +283,7 @@
        }
 
        /* Warn about scopes whose fetches cannot be evaluated at this action. 
*/
-       flt_otel_group_check_loc(conf, group_id, px, 
flt_otel_group_data[i].smp_val);
+       (void)flt_otel_group_check_loc(conf, group_id, px, 
flt_otel_group_data[i].smp_val);
 
        OTELC_SFREE_CLEAR(rule->arg.act.p[FLT_OTEL_ARG_FILTER_ID]);
        OTELC_SFREE_CLEAR(rule->arg.act.p[FLT_OTEL_ARG_GROUP_ID]);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/src/parser.c 
new/haproxy-opentelemetry-6344dfa/src/parser.c
--- old/haproxy-opentelemetry-ce86d09/src/parser.c      2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/src/parser.c      2026-06-24 
22:15:31.000000000 +0200
@@ -209,13 +209,14 @@
  *   flt_otel_parse_cfg_check - configuration keyword validation
  *
  * SYNOPSIS
- *   static int flt_otel_parse_cfg_check(const char *file, int line, char 
**args, const void *id, const struct flt_otel_parse_data *parse_data, size_t 
parse_data_size, const struct flt_otel_parse_data **pdata, char **err)
+ *   static int flt_otel_parse_cfg_check(const char *file, int line, char 
**args, const void *cur_obj, bool flag_need_instr, const struct 
flt_otel_parse_data *parse_data, size_t parse_data_size, const struct 
flt_otel_parse_data **pdata, char **err)
  *
  * ARGUMENTS
  *   file            - configuration file path
  *   line            - configuration file line number
  *   args            - configuration line arguments array
- *   id              - parent section identifier
+ *   cur_obj         - the section's currently open object, or NULL
+ *   flag_need_instr - whether the instrumentation must already be defined
  *   parse_data      - keyword definition table
  *   parse_data_size - number of entries in <parse_data>
  *   pdata           - output pointer to the matched keyword entry
@@ -223,19 +224,20 @@
  *
  * DESCRIPTION
  *   Common validation for configuration keywords.  Looks up <args[0]> in the
- *   <parse_data> table, checks argument count bounds, validates the first
- *   argument's characters according to the keyword's check_name type, and
- *   verifies that the parent section ID is set when required.
+ *   <parse_data> table, checks the argument count bounds, and validates the
+ *   first argument's characters according to the keyword's check_name type.
+ *   When <flag_need_instr> is set and the section object is open, it also
+ *   requires that the instrumentation has already been defined.
  *
  * RETURN VALUE
  *   Returns ERR_NONE (== 0) in case of success,
  *   or a combination of ERR_* flags if an error is encountered.
  */
-static int flt_otel_parse_cfg_check(const char *file, int line, char **args, 
const void *id, const struct flt_otel_parse_data *parse_data, size_t 
parse_data_size, const struct flt_otel_parse_data **pdata, char **err)
+static int flt_otel_parse_cfg_check(const char *file, int line, char **args, 
const void *cur_obj, bool flag_need_instr, const struct flt_otel_parse_data 
*parse_data, size_t parse_data_size, const struct flt_otel_parse_data **pdata, 
char **err)
 {
        int i, argc = 0, retval = ERR_NONE;
 
-       OTELC_FUNC("\"%s\", %d, %p, %p, %p, %zu, %p:%p, %p:%p", 
OTELC_STR_ARG(file), line, args, id, parse_data, parse_data_size, 
OTELC_DPTR_ARGS(pdata), OTELC_DPTR_ARGS(err));
+       OTELC_FUNC("\"%s\", %d, %p, %p, %hhu, %p, %zu, %p:%p, %p:%p", 
OTELC_STR_ARG(file), line, args, cur_obj, flag_need_instr, parse_data, 
parse_data_size, OTELC_DPTR_ARGS(pdata), OTELC_DPTR_ARGS(err));
 
        FLT_OTEL_ARGS_DUMP();
 
@@ -251,10 +253,10 @@
        else
                argc = flt_otel_args_count((const char **)args);
 
-       if ((retval & ERR_CODE) || (id == NULL))
+       if ((retval & ERR_CODE) || (cur_obj == NULL))
                /* Do nothing. */;
-       else if ((id != flt_otel_current_instr) && 
(flt_otel_current_config->instr == NULL))
-               FLT_OTEL_PARSE_ERR(err, "instrumentation not defined");
+       else if (flag_need_instr && (flt_otel_current_config->instr == NULL))
+               FLT_OTEL_PARSE_ERR(err, "'%s' : instrumentation not defined", 
args[0]);
 
        /*
         * Checking that fewer arguments are specified in the configuration
@@ -281,14 +283,6 @@
                        FLT_OTEL_PARSE_ERR(err, "%s '%s' : invalid character 
'%c'", args[0], args[1], *ic);
        }
 
-       /*
-        * The keywords that set flag_check_id attach to a span, so name the
-        * 'span' keyword when none is open.  flag_check_id is used only by
-        * the scope table, which makes this branch scope-specific.
-        */
-       if (!(retval & ERR_CODE) && (*pdata)->flag_check_id && (id == NULL))
-               FLT_OTEL_PARSE_ERR(err, "'%s' : %s ID not set (use '%s%s')", 
args[0], parse_data[FLT_OTEL_PARSE_SCOPE_SPAN].name, 
parse_data[FLT_OTEL_PARSE_SCOPE_SPAN].name, 
parse_data[FLT_OTEL_PARSE_SCOPE_SPAN].usage);
-
        OTELC_RETURN_INT(retval);
 }
 
@@ -649,6 +643,45 @@
 
 /***
  * NAME
+ *   flt_otel_parse_cfg_acl - acl keyword parser
+ *
+ * SYNOPSIS
+ *   static int flt_otel_parse_cfg_acl(const char *file, int line, char 
**args, struct list *acls, char **err)
+ *
+ * ARGUMENTS
+ *   file - configuration file path
+ *   line - configuration file line number
+ *   args - configuration line arguments array
+ *   acls - list head receiving the parsed ACL
+ *   err  - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ *   Parses the 'acl' keyword shared by the otel-instrumentation and otel-scope
+ *   sections.  The reserved word 'or' is rejected as an ACL name; otherwise 
the
+ *   declaration is handed to HAProxy's parse_acl() and the result is appended
+ *   to <acls>.
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_otel_parse_cfg_acl(const char *file, int line, char **args, 
struct list *acls, char **err)
+{
+       int retval = ERR_NONE;
+
+       OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, 
args, acls, OTELC_DPTR_ARGS(err));
+
+       if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
+               FLT_OTEL_PARSE_ERR(err, "'%s %s ...' : invalid ACL name", 
args[0], args[1]);
+       else if (parse_acl((const char **)args + 1, acls, err, 
&(flt_otel_current_config->proxy->conf.args), file, line) == NULL)
+               retval |= ERR_ABORT | ERR_ALERT;
+
+       OTELC_RETURN_INT(retval);
+}
+
+
+/***
+ * NAME
  *   flt_otel_parse_cfg_instr - otel-instrumentation section parser
  *
  * SYNOPSIS
@@ -684,7 +717,7 @@
                OTELC_RETURN_INT(retval);
 
        /* Validate and identify the instrumentation keyword. */
-       retval = flt_otel_parse_cfg_check(file, line, args, 
flt_otel_current_instr, parse_data, OTELC_TABLESIZE(parse_data), &pdata, &err);
+       retval = flt_otel_parse_cfg_check(file, line, args, 
flt_otel_current_instr, false, parse_data, OTELC_TABLESIZE(parse_data), &pdata, 
&err);
        if (retval & ERR_CODE) {
                FLT_OTEL_PARSE_IFERR_ALERT();
 
@@ -727,10 +760,7 @@
                                retval |= ERR_ABORT | ERR_ALERT;
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_ACL) {
-               if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
-                       FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : invalid ACL 
name", args[0], args[1]);
-               else if (parse_acl((const char **)args + 1, 
&(flt_otel_current_instr->acls), &err, 
&(flt_otel_current_config->proxy->conf.args), file, line) == NULL)
-                       retval |= ERR_ABORT | ERR_ALERT;
+               retval = flt_otel_parse_cfg_acl(file, line, args, 
&(flt_otel_current_instr->acls), &err);
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_RATE_LIMIT) {
                double value;
@@ -756,7 +786,7 @@
                                flt_otel_current_instr->logging |= 
FLT_OTEL_LOGGING_NOLOGNORM;
                }
                else
-                       FLT_OTEL_PARSE_ERR(&err, "'%s' : unknown option '%s'", 
args[0], args[1]);
+                       FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid option '%s'", 
args[0], args[1]);
        }
 #ifdef DEBUG_OTEL
        else if (pdata->keyword == FLT_OTEL_PARSE_INSTR_DEBUG_LEVEL) {
@@ -858,7 +888,7 @@
                OTELC_RETURN_INT(retval);
 
        /* Validate and identify the group keyword. */
-       retval = flt_otel_parse_cfg_check(file, line, args, 
flt_otel_current_group, parse_data, OTELC_TABLESIZE(parse_data), &pdata, &err);
+       retval = flt_otel_parse_cfg_check(file, line, args, 
flt_otel_current_group, true, parse_data, OTELC_TABLESIZE(parse_data), &pdata, 
&err);
        if (retval & ERR_CODE) {
                FLT_OTEL_PARSE_IFERR_ALERT();
 
@@ -925,6 +955,42 @@
 
 /***
  * NAME
+ *   flt_otel_parse_ctx_flag - context storage type token lookup
+ *
+ * SYNOPSIS
+ *   static uint8_t flt_otel_parse_ctx_flag(const char *arg)
+ *
+ * ARGUMENTS
+ *   arg - the storage type token to recognize
+ *
+ * DESCRIPTION
+ *   Recognizes a span context storage type token shared by the 'inject' and
+ *   'extract' keywords.  "use-headers" selects HTTP header storage and, when
+ *   USE_OTEL_VARS is defined, "use-vars" selects HAProxy variable storage.
+ *
+ * RETURN VALUE
+ *   Returns the matching FLT_OTEL_CTX_USE_* flag, or 0 when the token is not a
+ *   recognized storage type.
+ */
+static uint8_t flt_otel_parse_ctx_flag(const char *arg)
+{
+       uint8_t retval = 0;
+
+       OTELC_FUNC("\"%s\"", OTELC_STR_ARG(arg));
+
+       if (strcmp(arg, FLT_OTEL_PARSE_CTX_USE_HEADERS) == 0)
+               retval = FLT_OTEL_CTX_USE_HEADERS;
+#ifdef USE_OTEL_VARS
+       else if (strcmp(arg, FLT_OTEL_PARSE_CTX_USE_VARS) == 0)
+               retval = FLT_OTEL_CTX_USE_VARS;
+#endif
+
+       OTELC_RETURN_EX(retval, uint8_t, "0x%02hhx");
+}
+
+
+/***
+ * NAME
  *   flt_otel_parse_cfg_scope_ctx - context storage type parser
  *
  * SYNOPSIS
@@ -936,9 +1002,10 @@
  *   err     - indirect pointer to error message string
  *
  * DESCRIPTION
- *   Parses the context storage type argument for inject/extract keywords.
- *   Accepts "use-headers" or (when USE_OTEL_VARS is defined) "use-vars".
- *   Both types may be used simultaneously on the same span.
+ *   Parses a context storage type token for the 'inject' keyword and records 
it
+ *   on the current span.  Token recognition is delegated to the shared helper
+ *   flt_otel_parse_ctx_flag(); both supported types may be set on the same 
span
+ *   but neither may be repeated.
  *
  * RETURN VALUE
  *   Returns ERR_NONE (== 0) in case of success,
@@ -946,22 +1013,14 @@
  */
 static int flt_otel_parse_cfg_scope_ctx(char **args, int cur_arg, char **err)
 {
-       uint8_t flags = 0;
+       uint8_t flags;
        int     retval = ERR_NONE;
 
        OTELC_FUNC("%p, %d, %p:%p", args, cur_arg, OTELC_DPTR_ARGS(err));
 
-       if (FLT_OTEL_PARSE_KEYWORD(cur_arg, FLT_OTEL_PARSE_CTX_USE_HEADERS))
-               flags = FLT_OTEL_CTX_USE_HEADERS;
-#ifdef USE_OTEL_VARS
-       else if (FLT_OTEL_PARSE_KEYWORD(cur_arg, FLT_OTEL_PARSE_CTX_USE_VARS))
-               flags = FLT_OTEL_CTX_USE_VARS;
-#endif
-       else
-               FLT_OTEL_PARSE_ERR(err, "'%s' : invalid context storage type", 
args[0]);
-
+       flags = flt_otel_parse_ctx_flag(args[cur_arg]);
        if (flags == 0)
-               /* Do nothing. */;
+               FLT_OTEL_PARSE_ERR(err, "'%s' : invalid context storage type", 
args[0]);
        else if (flt_otel_current_span->ctx_flags & flags)
                FLT_OTEL_PARSE_ERR(err, "'%s' : %s already used", args[0], 
args[cur_arg]);
        else
@@ -1100,6 +1159,47 @@
 
 /***
  * NAME
+ *   flt_otel_parse_trailing_cond - attach a mandatory if/unless condition
+ *
+ * SYNOPSIS
+ *   static int flt_otel_parse_trailing_cond(const char *file, int line, char 
**args, int cond_pos, struct acl_cond **cond, char **err)
+ *
+ * ARGUMENTS
+ *   file     - configuration file path
+ *   line     - configuration file line number
+ *   args     - configuration line arguments array
+ *   cond_pos - args[] position that must hold the 'if'/'unless' keyword
+ *   cond     - destination for the built ACL condition
+ *   err      - indirect pointer to error message string
+ *
+ * DESCRIPTION
+ *   Handles a directive's trailing clause where the only argument still 
allowed
+ *   is an optional 'if'/'unless' condition.  When <args[cond_pos]> is such a
+ *   keyword, the condition is built via flt_otel_parse_attach_cond() into
+ *   <*cond>; otherwise a uniform "expects 'if' or 'unless'" error is reported.
+ *   The caller verifies that an argument is present at <cond_pos>.
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_otel_parse_trailing_cond(const char *file, int line, char 
**args, int cond_pos, struct acl_cond **cond, char **err)
+{
+       int retval = ERR_NONE;
+
+       OTELC_FUNC("\"%s\", %d, %p, %d, %p, %p:%p", OTELC_STR_ARG(file), line, 
args, cond_pos, cond, OTELC_DPTR_ARGS(err));
+
+       if (FLT_OTEL_ARG_ISCOND(cond_pos))
+               retval = flt_otel_parse_attach_cond(file, line, args, cond_pos, 
cond, err);
+       else
+               FLT_OTEL_PARSE_ERR(err, "'%s' : expects either 'if' or 'unless' 
followed by a condition but found '%s'", args[0], args[cond_pos]);
+
+       OTELC_RETURN_INT(retval);
+}
+
+
+/***
+ * NAME
  *   flt_otel_parse_cfg_sample_cond - sample definition with optional condition
  *
  * SYNOPSIS
@@ -1393,7 +1493,7 @@
                                flag_add_attr = true;
                        }
                        else {
-                               FLT_OTEL_PARSE_ERR(err, "'%s' : unknown keyword 
(use '%s%s')", args[i], pdata->name, pdata->usage);
+                               FLT_OTEL_PARSE_ERR(err, "'%s' : invalid 
argument (use '%s%s')", args[i], pdata->name, pdata->usage);
                        }
                }
 
@@ -1627,7 +1727,7 @@
 static int flt_otel_parse_cfg_exception(const char *file, int line, char 
**args, const struct flt_otel_parse_data *pdata, char **err)
 {
        struct flt_otel_conf_exception *exc;
-       int                             i, cond_pos = 0, retval = ERR_NONE;
+       int                             i, retval = ERR_NONE;
 
        OTELC_FUNC("\"%s\", %d, %p, %p, %p:%p", OTELC_STR_ARG(file), line, 
args, pdata, OTELC_DPTR_ARGS(err));
 
@@ -1675,12 +1775,7 @@
                }
                else {
                        /* The remaining argument must be a trailing condition. 
*/
-                       cond_pos = flt_otel_find_cond_pos(args, i);
-
-                       if (cond_pos == i)
-                               retval = flt_otel_parse_attach_cond(file, line, 
args, cond_pos, &(exc->cond), err);
-                       else
-                               FLT_OTEL_PARSE_ERR(err, "'%s' : unexpected 
argument (use '%s%s')", args[i], pdata->name, pdata->usage);
+                       retval = flt_otel_parse_trailing_cond(file, line, args, 
i, &(exc->cond), err);
 
                        break;
                }
@@ -1759,7 +1854,7 @@
        }
 
        if (name_len >= sizeof(field_name)) {
-               FLT_OTEL_PARSE_ERR(err, "'%s' : unknown field '%s'", args[0], 
args[3]);
+               FLT_OTEL_PARSE_ERR(err, "'%s' : invalid field '%s'", args[0], 
args[3]);
 
                OTELC_FREE(field_key);
 
@@ -1771,7 +1866,7 @@
 
        kw = flt_otel_kw_lookup(fields, OTELC_TABLESIZE(fields), field_name);
        if (kw == NULL) {
-               FLT_OTEL_PARSE_ERR(err, "'%s' : unknown field '%s'", args[0], 
field_name);
+               FLT_OTEL_PARSE_ERR(err, "'%s' : invalid field '%s'", args[0], 
field_name);
 
                OTELC_FREE(field_key);
 
@@ -1788,12 +1883,8 @@
                FLT_OTEL_PARSE_ERR(err, "'%s' : field '%s' does not take a 
key", args[0], field_name);
 
        /* An optional trailing 'if'/'unless' condition gates the assignment. */
-       if (!(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(4)) {
-               if (FLT_OTEL_ARG_ISCOND(4))
-                       retval = flt_otel_parse_attach_cond(file, line, args, 
4, &(conf->cond), err);
-               else
-                       FLT_OTEL_PARSE_ERR(err, "'%s' : unexpected argument 
'%s'", args[0], args[4]);
-       }
+       if (!(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(4))
+               retval = flt_otel_parse_trailing_cond(file, line, args, 4, 
&(conf->cond), err);
 
        OTELC_RETURN_INT(retval);
 }
@@ -1814,8 +1905,9 @@
  *
  * DESCRIPTION
  *   Parses an 'unset-var' directive: one or more variable names optionally
- *   followed by an 'if'/'unless' ACL condition.  The names are stored in a new
- *   conf_unset_var directive appended to the current scope, and the condition,
+ *   followed by an 'if'/'unless' ACL condition.  Each name is validated and
+ *   registered with the variable subsystem, as for 'set-var', then stored in a
+ *   new conf_unset_var directive appended to the current scope; the condition,
  *   when present, gates the removal of all of them at runtime.
  *
  * RETURN VALUE
@@ -1846,7 +1938,9 @@
 
        /* Store the variable names up to the condition, or to the end of line. 
*/
        for (i = 1; !(retval & ERR_CODE) && FLT_OTEL_ARG_ISVALID(i) && 
((cond_pos == 0) || (i < cond_pos)); i++)
-               if (flt_otel_conf_str_init(args[i], line, &(unset_var->vars), 
err) == NULL)
+               if (flt_otel_var_register_byname(args[i], err) == 
FLT_OTEL_RET_ERROR)
+                       retval |= ERR_ABORT | ERR_ALERT;
+               else if (flt_otel_conf_str_init(args[i], line, 
&(unset_var->vars), err) == NULL)
                        retval |= ERR_ABORT | ERR_ALERT;
 
        if (!(retval & ERR_CODE) && (cond_pos != 0))
@@ -1897,7 +1991,16 @@
                OTELC_RETURN_INT(retval);
 
        /* Validate and identify the scope keyword. */
-       retval = flt_otel_parse_cfg_check(file, line, args, 
flt_otel_current_span, parse_data, OTELC_TABLESIZE(parse_data), &pdata, &err);
+       retval = flt_otel_parse_cfg_check(file, line, args, 
flt_otel_current_span, true, parse_data, OTELC_TABLESIZE(parse_data), &pdata, 
&err);
+
+       /*
+        * Keywords that set flag_check_id attach to a span, so name the 'span'
+        * keyword when none is open.  This check is scope-specific, so it lives
+        * here rather than in the generic keyword validator.
+        */
+       if (!(retval & ERR_CODE) && pdata->flag_check_id && 
(flt_otel_current_span == NULL))
+               FLT_OTEL_PARSE_ERR(&err, "'%s' : %s ID not set (use '%s%s')", 
args[0], parse_data[FLT_OTEL_PARSE_SCOPE_SPAN].name, 
parse_data[FLT_OTEL_PARSE_SCOPE_SPAN].name, 
parse_data[FLT_OTEL_PARSE_SCOPE_SPAN].usage);
+
        if (retval & ERR_CODE) {
                FLT_OTEL_PARSE_IFERR_ALERT();
 
@@ -2154,32 +2257,15 @@
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_INJECT) {
                /*
-                * The context name is automatically generated when the macro
-                * FLT_OTEL_PARSE_CTX_AUTONAME is used as the name.
-                *
-                * In this case, if the inject directive is defined after the
-                * event definition, the event name is used as the context name;
-                * otherwise, the context name is taken from the span name.
+                * The context name is stored verbatim.  The autoname sentinel
+                * (FLT_OTEL_PARSE_CTX_AUTONAME) is resolved in the post-parse
+                * phase, where the scope event is known regardless of whether
+                * 'inject' precedes or follows the 'otel-event' directive.
                 */
                if (flt_otel_current_span->ctx_id != NULL)
                        FLT_OTEL_PARSE_ERR(&err, "'%s' : only one context per 
span is allowed", args[1]);
-               else if (!FLT_OTEL_PARSE_KEYWORD(1, 
FLT_OTEL_PARSE_CTX_AUTONAME))
+               else
                        retval = 
flt_otel_parse_strdup(&(flt_otel_current_span->ctx_id), 
&(flt_otel_current_span->ctx_id_len), args[1], &err, args[0]);
-               else if (flt_otel_current_scope->event != FLT_OTEL_EVENT__NONE)
-                       retval = 
flt_otel_parse_strdup(&(flt_otel_current_span->ctx_id), 
&(flt_otel_current_span->ctx_id_len), 
flt_otel_event_data[flt_otel_current_scope->event].name, &err, args[0]);
-               else {
-                       const char *ch;
-
-                       /*
-                        * When the context name is derived from the span name,
-                        * only the following characters are valid: [A-Za-z_.-].
-                        */
-                       ch = invalid_prefix_char(flt_otel_current_span->id);
-                       if (ch == NULL)
-                               retval = 
flt_otel_parse_strdup(&(flt_otel_current_span->ctx_id), 
&(flt_otel_current_span->ctx_id_len), flt_otel_current_span->id, &err, args[0]);
-                       else
-                               FLT_OTEL_PARSE_ERR(&err, "'%s' : character '%c' 
is not permitted in the context name", flt_otel_current_span->id, *ch);
-               }
 
                if (flt_otel_current_span->ctx_id != NULL) {
                        /*
@@ -2197,30 +2283,33 @@
                                flt_otel_current_span->ctx_flags = 
FLT_OTEL_CTX_USE_HEADERS;
                        }
 
-                       if (flt_otel_current_span->ctx_flags & 
FLT_OTEL_CTX_USE_VARS)
+                       /*
+                        * An explicit name is warned about now; the autoname's
+                        * warning is deferred to the post-parse phase, where it
+                        * is emitted against the resolved name.
+                        */
+                       if ((flt_otel_current_span->ctx_flags & 
FLT_OTEL_CTX_USE_VARS) && !FLT_OTEL_PARSE_KEYWORD(1, 
FLT_OTEL_PARSE_CTX_AUTONAME))
                                flt_otel_parse_ctx_name_warn(file, line, 
args[0], flt_otel_current_span->ctx_id);
                }
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_EXTRACT) {
                struct flt_otel_conf_context *conf_ctx;
+               uint8_t                       flags = FLT_OTEL_CTX_USE_HEADERS;
 
                /*
                 * Here is checked the context storage type; which, if
                 * not explicitly specified, is set to HTTP headers.
                 */
                conf_ctx = flt_otel_conf_context_init(args[1], line, 
&(flt_otel_current_scope->contexts), &err);
+               if (FLT_OTEL_ARG_ISVALID(2))
+                       flags = flt_otel_parse_ctx_flag(args[2]);
+
                if (conf_ctx == NULL)
                        retval |= ERR_ABORT | ERR_ALERT;
-               else if (!FLT_OTEL_ARG_ISVALID(2))
-                       conf_ctx->flags = FLT_OTEL_CTX_USE_HEADERS;
-               else if (FLT_OTEL_PARSE_KEYWORD(2, 
FLT_OTEL_PARSE_CTX_USE_HEADERS))
-                       conf_ctx->flags = FLT_OTEL_CTX_USE_HEADERS;
-#ifdef USE_OTEL_VARS
-               else if (FLT_OTEL_PARSE_KEYWORD(2, FLT_OTEL_PARSE_CTX_USE_VARS))
-                       conf_ctx->flags = FLT_OTEL_CTX_USE_VARS;
-#endif
-               else
+               else if (flags == 0)
                        FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid context 
storage type", args[2]);
+               else
+                       conf_ctx->flags = flags;
 
                if ((conf_ctx != NULL) && (conf_ctx->flags & 
FLT_OTEL_CTX_USE_VARS))
                        flt_otel_parse_ctx_name_warn(file, line, args[0], 
args[1]);
@@ -2236,12 +2325,8 @@
                 * condition is built the same way as for the 'otel-event'
                 * keyword.
                 */
-               if (!FLT_OTEL_ARG_ISVALID(1))
-                       /* Do nothing. */;
-               else if (FLT_OTEL_ARG_ISCOND(1))
-                       retval = flt_otel_parse_attach_cond(file, line, args, 
1, &(flt_otel_current_scope->stop_cond), &err);
-               else
-                       FLT_OTEL_PARSE_ERR(&err, "'%s' : expects either 'if' or 
'unless' followed by a condition but found '%s'", args[0], args[1]);
+               if (FLT_OTEL_ARG_ISVALID(1))
+                       retval = flt_otel_parse_trailing_cond(file, line, args, 
1, &(flt_otel_current_scope->stop_cond), &err);
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_INSTRUMENT) {
                retval = flt_otel_parse_cfg_instrument(file, line, args, pdata, 
&err);
@@ -2250,10 +2335,7 @@
                retval = flt_otel_parse_cfg_log_record(file, line, args, pdata, 
&err);
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ACL) {
-               if (FLT_OTEL_PARSE_KEYWORD(1, "or"))
-                       FLT_OTEL_PARSE_ERR(&err, "'%s %s ...' : invalid ACL 
name", args[0], args[1]);
-               else if (parse_acl((const char **)args + 1, 
&(flt_otel_current_scope->acls), &err, 
&(flt_otel_current_config->proxy->conf.args), file, line) == NULL)
-                       retval |= ERR_ABORT | ERR_ALERT;
+               retval = flt_otel_parse_cfg_acl(file, line, args, 
&(flt_otel_current_scope->acls), &err);
        }
        else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_IDLE_TIMEOUT) {
                const char *res;
@@ -2261,7 +2343,7 @@
 
                res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
                if (flt_otel_current_scope->idle_timeout != 0)
-                       FLT_OTEL_PARSE_ERR(&err, "'%s' : already set", args[0]);
+                       FLT_OTEL_PARSE_ERR(&err, "'%s' : already set (use 
'%s%s')", args[0], pdata->name, pdata->usage);
                else if (res == PARSE_TIME_OVER)
                        FLT_OTEL_PARSE_ERR(&err, "'%s' : timer overflow in 
argument '%s'", args[0], args[1]);
                else if (res == PARSE_TIME_UNDER)
@@ -2276,7 +2358,7 @@
        else if (pdata->keyword == FLT_OTEL_PARSE_SCOPE_ON_EVENT) {
                /* Scope can only have one event defined. */
                if (flt_otel_current_scope->event != FLT_OTEL_EVENT__NONE) {
-                       FLT_OTEL_PARSE_ERR(&err, "'%s' : event already set", 
flt_otel_current_scope->id);
+                       FLT_OTEL_PARSE_ERR(&err, "'%s' : already set (use 
'%s%s')", args[0], pdata->name, pdata->usage);
                } else {
                        /* Check the event name. */
                        for (i = 0; i < OTELC_TABLESIZE(flt_otel_event_data); 
i++)
@@ -2291,13 +2373,9 @@
                         * is checked here.
                         */
                        if (flt_otel_current_scope->event == 
FLT_OTEL_EVENT__NONE)
-                               FLT_OTEL_PARSE_ERR(&err, "'%s' : unknown 
event", args[1]);
-                       else if (!FLT_OTEL_ARG_ISVALID(2))
-                               /* Do nothing. */;
-                       else if (FLT_OTEL_ARG_ISCOND(2))
-                               retval = flt_otel_parse_attach_cond(file, line, 
args, 2, &(flt_otel_current_scope->cond), &err);
-                       else
-                               FLT_OTEL_PARSE_ERR(&err, "'%s' : expects either 
'if' or 'unless' followed by a condition but found '%s'", args[1], args[2]);
+                               FLT_OTEL_PARSE_ERR(&err, "'%s' : invalid 
event", args[1]);
+                       else if (FLT_OTEL_ARG_ISVALID(2))
+                               retval = flt_otel_parse_trailing_cond(file, 
line, args, 2, &(flt_otel_current_scope->cond), &err);
 
                        if (!(retval & ERR_CODE))
                                OTELC_DBG(DEBUG, "event '%s'", args[1]);
@@ -2338,6 +2416,84 @@
 
 /***
  * NAME
+ *   flt_otel_post_parse_ctx_autoname - resolve a deferred autoname context
+ *
+ * SYNOPSIS
+ *   static int flt_otel_post_parse_ctx_autoname(struct flt_otel_conf_span 
*conf_span)
+ *
+ * ARGUMENTS
+ *   conf_span - the span whose autoname context is resolved
+ *
+ * DESCRIPTION
+ *   Resolves a span context name that 'inject' deferred at parse time with the
+ *   FLT_OTEL_PARSE_CTX_AUTONAME sentinel.  Runs in the otel-scope post-parse
+ *   phase, where the scope event is known regardless of whether 'inject'
+ *   preceded or followed 'otel-event'.  The scope event name is preferred; the
+ *   span name is used only when the scope has no event and must then be a 
valid
+ *   context prefix.  The resolved name keeps the 
FLT_OTEL_PARSE_CTX_IGNORE_NAME
+ *   prefix, so the injected headers carry only the bare W3C propagation
+ *   headers.  When the context is stored in HAProxy variables, the 
variable-name
+ *   warning is emitted here against the resolved name.
+ *
+ * RETURN VALUE
+ *   Returns ERR_NONE (== 0) in case of success,
+ *   or a combination of ERR_* flags if an error is encountered.
+ */
+static int flt_otel_post_parse_ctx_autoname(struct flt_otel_conf_span 
*conf_span)
+{
+       const char *name = NULL, *ch;
+       char       *resolved;
+       size_t      len;
+       int         retval = ERR_NONE;
+
+       OTELC_FUNC("%p", conf_span);
+
+       if (flt_otel_current_scope->event != FLT_OTEL_EVENT__NONE) {
+               name = flt_otel_event_data[flt_otel_current_scope->event].name;
+       } else {
+               /*
+                * The span name fallback is only valid as a context prefix
+                * when it has only the characters [A-Za-z_.-].
+                */
+               ch = invalid_prefix_char(conf_span->id);
+               if (ch == NULL)
+                       name = conf_span->id;
+               else
+                       FLT_OTEL_POST_PARSE_ALERT("inject '%s' : character '%c' 
is not permitted in the context name", conf_span->cfg_line, conf_span->id, *ch);
+       }
+
+       if (name == NULL)
+               OTELC_RETURN_INT(retval);
+
+       /*
+        * The generated name keeps the FLT_OTEL_PARSE_CTX_IGNORE_NAME prefix
+        * so the injected headers carry no HAProxy-specific name, leaving only
+        * the bare W3C propagation headers.
+        */
+       len      = strlen(name);
+       resolved = OTELC_MALLOC(len + 2);
+       if (resolved == NULL) {
+               FLT_OTEL_POST_PARSE_ALERT("inject '%s' : out of memory", 
conf_span->cfg_line, name);
+
+               OTELC_RETURN_INT(retval);
+       }
+
+       *resolved = FLT_OTEL_PARSE_CTX_IGNORE_NAME;
+       (void)memcpy(resolved + 1, name, len + 1);
+
+       OTELC_SFREE(conf_span->ctx_id);
+       conf_span->ctx_id     = resolved;
+       conf_span->ctx_id_len = len + 1;
+
+       if (conf_span->ctx_flags & FLT_OTEL_CTX_USE_VARS)
+               flt_otel_parse_ctx_name_warn(flt_otel_current_config->cfg_file, 
conf_span->cfg_line, "inject", conf_span->ctx_id);
+
+       OTELC_RETURN_INT(retval);
+}
+
+
+/***
+ * NAME
  *   flt_otel_post_parse_cfg_scope - otel-scope post-parse check
  *
  * SYNOPSIS
@@ -2366,11 +2522,23 @@
        if (flt_otel_current_scope == NULL)
                OTELC_RETURN_INT(retval);
 
-       /* If span context inject is used, check that this is possible. */
-       list_for_each_entry(conf_span, &(flt_otel_current_scope->spans), list)
-               if ((conf_span->ctx_id != NULL) && (conf_span->ctx_flags & 
FLT_OTEL_CTX_USE_HEADERS))
+       /*
+        * Resolve any deferred autoname context, then verify that HTTP
+        * header injection is only used on events that support it.  The
+        * scope event is known here regardless of the directive order.
+        */
+       list_for_each_entry(conf_span, &(flt_otel_current_scope->spans), list) {
+               if ((conf_span->ctx_id != NULL) && (strcmp(conf_span->ctx_id, 
FLT_OTEL_PARSE_CTX_AUTONAME) == 0))
+                       retval |= flt_otel_post_parse_ctx_autoname(conf_span);
+
+               /*
+                * A context still holding the autoname sentinel failed to
+                * resolve and was already reported, so it is skipped here.
+                */
+               if ((conf_span->ctx_id != NULL) && (strcmp(conf_span->ctx_id, 
FLT_OTEL_PARSE_CTX_AUTONAME) != 0) && (conf_span->ctx_flags & 
FLT_OTEL_CTX_USE_HEADERS))
                        if 
(!flt_otel_event_data[flt_otel_current_scope->event].flag_http_inject)
                                FLT_OTEL_POST_PARSE_ALERT("inject '%s' : cannot 
use on this event", conf_span->cfg_line, conf_span->ctx_id);
+       }
 
        /* Validate idle-timeout / on-idle-timeout consistency. */
        if (flt_otel_current_scope->idle_timeout == 0) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/src/pool.c 
new/haproxy-opentelemetry-6344dfa/src/pool.c
--- old/haproxy-opentelemetry-ce86d09/src/pool.c        2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/src/pool.c        2026-06-24 
22:15:31.000000000 +0200
@@ -37,9 +37,11 @@
  *
  * DESCRIPTION
  *   Allocates <size> bytes of memory from the HAProxy memory <pool>.  If 
<pool>
- *   is NULL, the allocation falls back to the heap via OTELC_MALLOC().  When
- *   <flag_clear> is set, the allocated memory is zero-filled.  On allocation
- *   failure, an error message is stored via <err>.
+ *   is NULL, the allocation falls back to the heap via OTELC_MALLOC().  A
+ *   non-NULL <pool> returns a fixed-size element freed through the same pool,
+ *   so a request larger than <pool>->size is rejected rather than overrunning
+ *   the element.  When <flag_clear> is set, the allocated memory is
+ *   zero-filled.  On allocation failure, an error message is stored via <err>.
  *
  * RETURN VALUE
  *   Returns a pointer to the allocated memory, or NULL on failure.
@@ -51,6 +53,18 @@
        OTELC_FUNC("%p, %zu, %hhu, %p:%p", pool, size, flag_clear, 
OTELC_DPTR_ARGS(err));
 
        if (pool != NULL) {
+               /*
+                * The pool hands back a fixed-size element freed through the
+                * same pool; it cannot fall back to the heap for an oversize
+                * request without losing that provenance, so reject it rather
+                * than overrun the block.
+                */
+               if (size > pool->size) {
+                       FLT_OTEL_ERR("pool '%s' element size %u too small for 
%zu bytes", pool->name, pool->size, size);
+
+                       OTELC_RETURN_PTR(NULL);
+               }
+
                retptr = pool_alloc(pool);
                if (retptr != NULL)
                        OTELC_DBG(MEM, "POOL_ALLOC: %s:%d(%p %zu)", __func__, 
__LINE__, retptr, FLT_OTEL_DEREF(pool, size, size));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/test/be/otel.cfg 
new/haproxy-opentelemetry-6344dfa/test/be/otel.cfg
--- old/haproxy-opentelemetry-ce86d09/test/be/otel.cfg  2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/test/be/otel.cfg  2026-06-24 
22:15:31.000000000 +0200
@@ -16,6 +16,9 @@
         scopes http_response
         scopes server_session_end
 
+    # Extracting under the 'otel-ctx' name matches propagation headers that
+    # carry that prefix.  Prefix the name with '-' (extract "-otel-ctx") to
+    # read the bare W3C headers instead.
     otel-scope frontend_http_request
         extract "otel-ctx" use-headers
         span "HAProxy session" parent "otel-ctx" root
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/haproxy-opentelemetry-ce86d09/test/fe/otel.cfg 
new/haproxy-opentelemetry-6344dfa/test/fe/otel.cfg
--- old/haproxy-opentelemetry-ce86d09/test/fe/otel.cfg  2026-06-22 
02:54:04.000000000 +0200
+++ new/haproxy-opentelemetry-6344dfa/test/fe/otel.cfg  2026-06-24 
22:15:31.000000000 +0200
@@ -42,6 +42,10 @@
         finish "Frontend HTTP request"
         otel-event on-backend-tcp-request
 
+    # Injecting under the 'otel-ctx' name makes the propagation headers carry
+    # that prefix (otel-ctx-traceparent, ...).  Prefix the name with '-' 
(inject
+    # "-otel-ctx"), or use "-" alone for an auto-derived name, to emit instead
+    # the bare W3C headers that external services expect.
     otel-scope backend_http_request
         span "Backend HTTP request" parent "Backend TCP request"
         finish "Backend TCP request"

Reply via email to