Skip to content

ci: fix dynamic-import response truncation flake (proxy transport race) + socket instrumentation - #41399

Merged
ggazzo merged 15 commits into
developfrom
fix/keepalive-socket-reaping
Jul 18, 2026
Merged

ci: fix dynamic-import response truncation flake (proxy transport race) + socket instrumentation#41399
ggazzo merged 15 commits into
developfrom
fix/keepalive-socket-reaping

Conversation

@KevLehman

@KevLehman KevLehman commented Jul 15, 2026

Copy link
Copy Markdown
Member

Proposed changes (including videos or screenshots)

Fixes the years-old CI flake where e2e tests died with Target page, context or browser has been closed / Application Error / stuck boot spinners — all one bug: POST /__meteor__/dynamic-import/fetch responses truncated mid-body (ERR_INCOMPLETE_CHUNKED_ENCODING), poisoning the client module loader.

Root cause (established via pcap + socket forensics + traefik debug logs on two instrumented reproductions)

A race in Go's net/http transport inside traefik, independent of every pooling config: when the tiny dynamic-import POST is written in two TCP segments (headers, then ~57-107B body) and node answers within ~1ms (cached module tree), the transport treats the response as arriving before the request write finished, marks the connection dead, and closes it 0.3ms after the first response bytes — mid body copy (httputil: ReverseProxy read error during body copy: use of closed network connection, then net/http: abort Handler). The browser has already received the 200 headers, so nothing can retry, and the truncated body kills the module fetch. Packet captures showed byte-identical kill signatures on fresh conns (maxidleconnsperhost=-1) and healthy pooled conns — config knobs only modulated frequency.

Two secondary defects found en route:

  • the test-retry middleware was defined but never attached to the router (RetryAttempts was always 0);
  • maxidleconnsperhost=-1 (2024 workaround for meteor webapp's 5s idle-socket reaper) additionally exhausts ephemeral ports under load (dial tcp: cannot assign requested address → 502 bursts).

The fix

  1. Dedicated traefik router for /__meteor__/dynamic-import with buffering + retry: buffering makes the request body memory-resident so headers+body coalesce into a single upstream write — no write-in-progress window, no early-response race; retry replays any pre-header failure. Scoped to this route because suite-wide buffering breaks 404/CORS response semantics.
  2. HTTP_SOCKET_TIMEOUT_MS (opt-in env, CI sets 120s): detaches webapp's 5s reaper re-arming and raises socket/keepAlive/headers timeouts above traefik's 90s idle pool, so the proxy always closes pooled conns first and never reuses one node killed. Unset (prod) = no behavior change.
  3. test-retry actually attached; -1 flag removed (keep-alive pool restored); traefik added to the e2e failure log dump (it was missing).

Regression test

tests/end-to-end/api/http-response-truncation.ts: 30k concurrent POSTs of real dynamic-import bodies (captured from a failing run, incl. the recurring AppLayoutThemeWrapper.tsx request) through the CI traefik; zero truncations allowed, plus a fixture-staleness canary. Red on the broken config (502 burst in both API jobs), green with the fix.

Validation

Broken-config states each reproduced their failure signature on camera; fix state: 3 consecutive fully green CI runs (hammer + ~1350 e2e tests per run), zero truncation signatures in server/traefik logs. The diagnostic instrumentation used for the investigation (socket forensics hooks, traefik access/debug logs, tcpdump sidecar) was removed once the root cause was proven — this PR carries only the fixes and the regression test.

Issue(s)

Steps to test or reproduce

Revert the traefik labels in docker-compose-ci.yml (drop the rocketchat-dynimport router) and the API jobs fail the truncation hammer; restore them and it passes.

Further comments

Prod is unaffected: changes are CI compose/workflow config plus one env-gated code path (HTTP_SOCKET_TIMEOUT_MS) that is inert unless the env var is set.

Task: ARCH-2272

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability for dynamic module loading through the proxy, reducing the risk of truncated responses during high request volumes.
    • Tuned server connection and keep-alive timeouts to better support longer-lived connections.
    • Added routing, retry, and buffering improvements for dynamic import requests.
  • Tests
    • Added end-to-end coverage to verify large dynamic import responses complete successfully under concurrent traffic.

@dionisio-bot

dionisio-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot

changeset-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 3a6cff2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f5827d73-34e5-4806-94c9-2cd5cb1087a7

📥 Commits

Reviewing files that changed from the base of the PR and between 0e20907 and 3a6cff2.

📒 Files selected for processing (5)
  • .github/workflows/ci-test-e2e.yml
  • apps/meteor/server/startup/httpSocketTimeout.ts
  • apps/meteor/server/startup/index.ts
  • apps/meteor/tests/end-to-end/api/http-response-truncation.ts
  • docker-compose-ci.yml
📜 Recent review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (1/5)
  • GitHub Check: 🔨 Test UI (EE) / MongoDB 8.0 coverage (3/5)
  • GitHub Check: 🔨 Test API (EE) / MongoDB 8.0 coverage (1/1)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (1/4)
  • GitHub Check: 🔨 Test API Apps (node-runtime - EE) / MongoDB 8.0 coverage (1/1)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (2/4)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (3/4)
  • GitHub Check: 🔨 Test API Livechat (EE) / MongoDB 8.0 coverage (1/1)
  • GitHub Check: 🔨 Test UI (CE) / MongoDB 8.0 (4/4)
  • GitHub Check: 🔨 Test Federation Matrix
  • GitHub Check: 📦 Track Image Sizes
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Hacktron Security Check
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/startup/httpSocketTimeout.ts
  • apps/meteor/server/startup/index.ts
  • apps/meteor/tests/end-to-end/api/http-response-truncation.ts
🧠 Learnings (3)
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In the Rocket.Chat repository, do not reference Biome lint rules in code review feedback. Biome is not used even if biome.json exists; only reference Biome rules if there is explicit, project-wide usage documented. For TypeScript files, review lint implications without Biome guidance unless the project enables Biome rules.

Applied to files:

  • apps/meteor/server/startup/httpSocketTimeout.ts
  • apps/meteor/server/startup/index.ts
  • apps/meteor/tests/end-to-end/api/http-response-truncation.ts
📚 Learning: 2026-02-26T19:25:44.063Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 38778
File: packages/ui-voip/src/providers/useMediaSession.ts:192-192
Timestamp: 2026-02-26T19:25:44.063Z
Learning: In this repository (RocketChat/Rocket.Chat), Biome lint rules are not used even if a biome.json exists. When reviewing TypeScript files (e.g., packages/ui-voip/src/providers/useMediaSession.ts), ensure lint suggestions do not reference Biome-specific rules. Rely on general ESLint/TypeScript lint rules and project conventions instead.

Applied to files:

  • apps/meteor/server/startup/httpSocketTimeout.ts
  • apps/meteor/server/startup/index.ts
  • apps/meteor/tests/end-to-end/api/http-response-truncation.ts
📚 Learning: 2026-05-06T12:21:44.083Z
Learnt from: juliajforesti
Repo: RocketChat/Rocket.Chat PR: 40256
File: apps/meteor/client/components/CreateDiscussion/CreateDiscussion.tsx:121-149
Timestamp: 2026-05-06T12:21:44.083Z
Learning: Field wrappers in rocket.chat/fuselage-forms (Field, FieldLabel, FieldRow, FieldError, FieldHint) auto-create htmlFor/id associations, aria-describedby, and role="alert" for errors. Do not manually set htmlFor, id, aria-describedby, or role attributes when using these wrappers. This automatic wiring does not apply to plain rocket.chat/fuselage components, which require explicit ID wiring per the accessibility docs. In code reviews, prefer using fuselage-forms wrappers for form fields and verify there is no unnecessary manual ID/aria wiring in files that use these wrappers. If a component uses plain fuselage components, ensure proper id wiring as per docs.

Applied to files:

  • apps/meteor/server/startup/httpSocketTimeout.ts
  • apps/meteor/server/startup/index.ts
  • apps/meteor/tests/end-to-end/api/http-response-truncation.ts
🔇 Additional comments (3)
apps/meteor/server/startup/index.ts (1)

10-10: LGTM!

docker-compose-ci.yml (1)

32-34: LGTM!

Also applies to: 43-53, 232-233

.github/workflows/ci-test-e2e.yml (1)

323-326: LGTM!


Walkthrough

The PR configures Rocket.Chat socket timeouts and Traefik dynamic-import routing, buffering, and retries. It adds a high-concurrency truncation regression test and collects Traefik logs when E2E tests fail.

Changes

Dynamic import transport handling

Layer / File(s) Summary
Socket timeout and proxy routing
apps/meteor/server/startup/..., docker-compose-ci.yml
Rocket.Chat applies HTTP_SOCKET_TIMEOUT_MS, while Traefik gains dedicated dynamic-import routing, buffering, retry settings, and updated connection flags.
Truncation regression validation
apps/meteor/tests/end-to-end/api/http-response-truncation.ts, .github/workflows/ci-test-e2e.yml
Concurrent dynamic-import requests validate response integrity, and failed E2E runs collect Traefik logs.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested labels: type: bug

Suggested reviewers: ggazzo, sampaiodiego, d-gubert

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: fixing dynamic-import response truncation flakiness, with related proxy/socket work.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • ARCH-2272: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 71.42857% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.51%. Comparing base (0e20907) to head (3a6cff2).
⚠️ Report is 20 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #41399      +/-   ##
===========================================
- Coverage    68.52%   68.51%   -0.02%     
===========================================
  Files         4114     4129      +15     
  Lines       159688   160180     +492     
  Branches     28938    29230     +292     
===========================================
+ Hits        109432   109744     +312     
- Misses       45213    45339     +126     
- Partials      5043     5097      +54     
Flag Coverage Δ
e2e 58.99% <ø> (-0.02%) ⬇️
e2e-api 45.34% <71.42%> (-0.01%) ⬇️
unit 70.46% <ø> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@KevLehman

Copy link
Copy Markdown
Member Author

Closing: the premise didn't survive verification. CI already fronts the server with traefik (3000:80, maxidleconnsperhost=-1 → fresh upstream connection per request), so the 5s idle-socket reaper this suite targets is unreachable from CI traffic — the new tests pass on every API job here, and a local replica of the exact CI topology (traefik v3.6.6, same flags, official image) also passes them, while replaying the failing trace's request pattern through it 10k+ times under CPU throttle reproduced zero truncations. The keep-alive reaping is real and reproducible against a directly-exposed server (headers-then-RST at 5s idle), but we couldn't verify it is what truncates the dynamic-import responses in CI, so this isn't the fix for those symptoms. The e2e crash mechanism (chunked dynamic-import response cut after headers → React.lazy hard-crash) remains unattributed; next lead is instrumenting CI itself (traefik access logs aren't captured today, and traefik isn't in the failure log dump).

@KevLehman KevLehman closed this Jul 15, 2026
Recurring e2e flake: /__meteor__/dynamic-import/fetch dies with
ERR_INCOMPLETE_CHUNKED_ENCODING (headers sent, zero body bytes) and the
killer leg was never attributed. Instead of another speculative fix,
instrument CI so the next occurrence names it:

- traefik JSON access logs enabled in docker-compose-ci.yml and dumped
  on e2e failure (traefik was missing from the log dump step), telling
  apart upstream truncation from proxy/client aborts
- SOCKET_FORENSICS env flag: logs any response whose socket closes
  after headers but before the body finishes, response stream errors,
  and the stack of whoever destroys a socket mid-response
@KevLehman KevLehman reopened this Jul 16, 2026
@KevLehman
KevLehman force-pushed the fix/keepalive-socket-reaping branch from 2ce367d to fecf51a Compare July 16, 2026 21:20
@KevLehman KevLehman changed the title fix: keep-alive sockets reaped inside the browser connection-reuse window ci: instrument e2e CI for dynamic-import response truncation forensics Jul 16, 2026
First reproduction attributed the truncation to the traefik->node leg:
traefik aborts the response copy (net/http: abort Handler, OriginStatus 0)
while node either finished cleanly or ended the socket mid-stream via the
destroySoon path (destroy at writable finish, exactly 48KiB of body sent).
The destroy stack cannot name the original end() caller, so:

- patch Socket#end like Socket#destroy (shared in-flight teardown logger)
- log socket timeout events (webapp reaps sockets 5s idle by default) with
  socket age and pending bytes, to catch the reaper racing a reused conn
- include pendingBytes/ageMs on truncation events
…n both legs

Forensics v2 named node kill path: socketOnEnd — the peer half-closes
first and node then terminates the in-flight response. The remaining
question is which side breaks the connection and why; a headers-only
(-s 96) tcpdump sidecar sharing the traefik netns sees both the
docker-proxy<->traefik and traefik<->node legs. Uploaded only on
e2e failure.
…loses

Deterministic reproduction of the server-side link of the dynamic-import
truncation flake: a client that FINs after fully sending its request is
entitled to the complete response, but node (httpAllowHalfOpen=false, the
shape meteor webapp configures) aborts the in-flight chunked response via
socketOnEnd -> socket.end(). Two red cases (FIN before response, FIN after
headers) plus a green control. Red by design until the server tolerates
half-closing clients.
@KevLehman

Copy link
Copy Markdown
Member Author

Root cause found (2nd instrumented reproduction, run 29560562870, file-upload.spec.ts hang at 15:50:19Z)

All four capture layers align on the same packet:

  1. pcap (traefik netns): conn 172.18.0.6:36404 → 172.18.0.13:3000 — SYN at 19.0270, request sent, node returns first 840 bytes of the chunked response at 19.0296, traefik sends FIN at 19.0299 — 0.3ms later, mid-response, unprovoked (the downstream browser connection was healthy; no FIN/RST preceded it).
  2. traefik debug at the same instant: httputil: ReverseProxy read error during body copy: read tcp 172.18.0.6:36404->172.18.0.13:3000: use of closed network connection — Go's transport closed its own connection underneath the response-body reader, then the recovery middleware aborted (net/http: abort Handler).
  3. socket-forensics (node): socket-end-mid-response on a 3ms-old socket, 840 bytes written, caller socketOnEnd (node:_http_server:851) — node reacting to the peer FIN by killing its own write side (httpAllowHalfOpen=false), truncating the chunked response.
  4. browser trace: 10th dynamic-import POST of the page load gets 200 headers + truncated body → net::ERR_INCOMPLETE_CHUNKED_ENCODINGFailed to fetch from the module loader → page hangs → 60s test timeout.

Why the connection was in that mode at all

--serverstransport.maxidleconnsperhost=-1 (added Jul 2024, d15d315, to work around meteor webapp's 5s idle-socket reaper) puts Go's Transport in keep-alives-disabled mode upstream: one fresh TCP connection per request, closed as soon as the request is 'done'. That mode has a rare race where the transport tears the connection down while the ReverseProxy is still streaming the chunked body — that teardown is the flake.

Aggravator: the test-retry middleware is defined in the compose labels but never attached to the router (traefik.http.routers.rocketchat.middlewares is not set) — RetryAttempts is always 0, so nothing softens the blast.

Deterministic test

tests/unit/server/lib/httpHalfCloseTruncation.spec.ts (red by design): a client that half-closes after fully sending its request must still receive the complete response; node truncates instead — the exact server-side link of the chain. Control case (client keeps the connection open) is green.

Fix direction

  • Remove maxidleconnsperhost=-1 and re-enable upstream keep-alive, raising node's keep-alive timeout above traefik's idleConnTimeout (so the 5s reaper the flag was dodging can no longer bite first).
  • Attach the retry middleware to the router as defense in depth.

- drop serverstransport.maxidleconnsperhost=-1: its keep-alives-disabled
  mode is where Go transport rarely closes the upstream conn mid body
  copy, truncating chunked responses (RCA on the PR)
- set serverstransport.idleconntimeout=3s instead: traefik drops idle
  upstream conns before the meteor webapp 5s reaper (the reason the -1
  flag existed) can kill one it would reuse
- actually attach the test-retry middleware to the rocketchat router;
  it was defined but never referenced, so RetryAttempts was always 0
- skip the half-close truncation spec: red by design, documents the
  server-side behavior the config fix routes around
Replaces the half-close unit spec (tested node directly, so the proxy
config fix could never turn it green) with a proxy-level regression test:
30k concurrent POSTs of real dynamic-import bodies captured from a failing
CI run (including the recurring AppLayoutThemeWrapper.tsx request), zero
truncations allowed. Traefik fix temporarily reverted on this commit to
confirm the test goes red against the broken config; the fix commit follows.
- drop serverstransport.maxidleconnsperhost=-1: its one-conn-per-request
  mode both races the ReverseProxy body copy (use of closed network
  connection -> truncated chunked responses) and exhausts ephemeral ports
  under load (dial tcp: cannot assign requested address -> 502 bursts,
  reproduced by the new truncation hammer test)
- set serverstransport.idleconntimeout=3s instead: traefik drops idle
  upstream conns before the meteor webapp 5s reaper (the reason the -1
  flag existed) can kill one it would reuse
- attach the test-retry middleware to the rocketchat router; it was
  defined but never referenced, so nothing ever retried
The 3s idleConnTimeout traded the -1 body-copy race for the Go
idle-expiry-vs-checkout race (reproduced on CE UI shards: same
"use of closed network connection" on dynamic-import). No timeout value
wins the timing game against the webapp 5s reaper, so stop playing it:
default 90s idle pool, and buffering+retry middlewares so a request that
lands on a server-reaped conn (fails before response headers) is
replayed transparently, POST bodies included.
…ia env

Every proxy-side config permutation loses a timing game (measured on the
PR): -1 races the body copy and exhausts ports, a short idleConnTimeout
races idle expiry against checkout, and buffering+retry breaks response
semantics suite-wide. The only race-free arrangement is the server
outliving the proxy pool: HTTP_SOCKET_TIMEOUT_MS (CI compose sets 120s,
above traefik 90s idleConnTimeout) detaches webapp 5s reaper re-arming
and raises socket/keepAlive/headers timeouts, so traefik always closes
pooled conns first and can never reuse one node already killed. Unset
(prod) nothing changes.
…sponse race

pcap from the v3 run (pooled conn, reaper disabled) shows the same
signature as every reproduction: request written in two segments, server
responds within ~1ms, traefik FINs 0.3ms after the first response bytes
and RSTs the rest — go net/http treats the fast response as arriving
before the request write completed and kills the conn mid body copy.
Config knobs only modulated the frequency; the fix is removing the
window: a dedicated dynamic-import router with buffering makes the tiny
POST body memory-resident so headers+body coalesce into one upstream
write, and retry replays any pre-header failure. Scoped to that route
because suite-wide buffering broke 404/CORS semantics.
@KevLehman KevLehman changed the title ci: instrument e2e CI for dynamic-import response truncation forensics ci: fix dynamic-import response truncation flake (proxy transport race) + socket instrumentation Jul 18, 2026
@ggazzo ggazzo added this to the 8.7.0 milestone Jul 18, 2026
@ggazzo

ggazzo commented Jul 18, 2026

Copy link
Copy Markdown
Member

/jira ARCH-2228

@ggazzo
ggazzo marked this pull request as ready for review July 18, 2026 01:38
@ggazzo
ggazzo requested review from a team as code owners July 18, 2026 01:38

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="docker-compose-ci.yml">

<violation number="1" location="docker-compose-ci.yml:52">
P3: Since the dynamic-import route only receives POST requests, attaching `test-retry` here is effectively a no-op: Traefik's retry middleware skips non-idempotent methods unless `retryNonIdempotentMethod: true` is set, so all the actual retry protection for this fix comes from `test-buffer`'s `retryExpression`. Consider dropping `test-retry` from this router (to avoid implying it does work here) or explicitly setting `retryNonIdempotentMethod: true` on it if retrying the POST via this middleware is actually intended.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread docker-compose-ci.yml
# 404/CORS response semantics
traefik.http.routers.rocketchat-dynimport.rule: PathPrefix(`/__meteor__/dynamic-import`)
traefik.http.routers.rocketchat-dynimport.service: rocketchat
traefik.http.routers.rocketchat-dynimport.middlewares: test-buffer,test-retry

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: Since the dynamic-import route only receives POST requests, attaching test-retry here is effectively a no-op: Traefik's retry middleware skips non-idempotent methods unless retryNonIdempotentMethod: true is set, so all the actual retry protection for this fix comes from test-buffer's retryExpression. Consider dropping test-retry from this router (to avoid implying it does work here) or explicitly setting retryNonIdempotentMethod: true on it if retrying the POST via this middleware is actually intended.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At docker-compose-ci.yml, line 52:

<comment>Since the dynamic-import route only receives POST requests, attaching `test-retry` here is effectively a no-op: Traefik's retry middleware skips non-idempotent methods unless `retryNonIdempotentMethod: true` is set, so all the actual retry protection for this fix comes from `test-buffer`'s `retryExpression`. Consider dropping `test-retry` from this router (to avoid implying it does work here) or explicitly setting `retryNonIdempotentMethod: true` on it if retrying the POST via this middleware is actually intended.</comment>

<file context>
@@ -37,7 +40,17 @@ services:
+      # 404/CORS response semantics
+      traefik.http.routers.rocketchat-dynimport.rule: PathPrefix(`/__meteor__/dynamic-import`)
+      traefik.http.routers.rocketchat-dynimport.service: rocketchat
+      traefik.http.routers.rocketchat-dynimport.middlewares: test-buffer,test-retry
+      traefik.http.middlewares.test-buffer.buffering.retryExpression: IsNetworkError() && Attempts() < 4
     healthcheck:
</file context>

@ggazzo
ggazzo merged commit 7619669 into develop Jul 18, 2026
45 of 48 checks passed
@ggazzo
ggazzo deleted the fix/keepalive-socket-reaping branch July 18, 2026 02:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants