ci: the IMDS deadline tests need the request's worker to reach `connect` within 0.2 s, so a runner stall fails them (flaky NVX CLI / Windows)
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 75/100
Research direction
Start with the two named tests in scripts/test_host_telemetry.py and the deadline behavior in scripts/nvx_tools/host_telemetry.py. Run the tests, then use the reproduction script's delayed-worker setup to check how they behave when scheduling is delayed. Done means the tests reliably verify the deadline behavior without failing when the worker reaches connect late.
Written by the indexing model from the issue text.
Description
Summary
scripts.test_host_telemetry.AzureMetadataRequestTests.test_a_connect_still_in_progress_is_shut_down_at_the_deadline failed NVX CLI / Windows 6 times from 2026-10-28 to 2026-11-03. Each time it failed with AssertionError: False is not true at self.assertTrue(blocked.is_set()), and nothing else in the job failed except, once, its sibling AzureProvenanceTests.test_metadata_request_is_aborted_at_the_deadline. Since #523 added the tests on 2026-10-15, they account for 6 of the 23 failed NVX CLI / Windows jobs, of 1,335 that completed: about 1 in 220. Neither failed on Linux.
Both tests give read_azure_compute_metadata a 0.2 s deadline and need the request's worker thread to reach connect before it. When a hosted runner does not run the worker within 0.2 s, the deadline cuts the request off first, and the worker's late connect is refused. The request still raises the expected TimeoutError, but the tests' checks that a connect was in progress fail. Delaying the worker by 0.25 s reproduces both failures exactly. The code under test behaves as designed in either order, so the defect is in the tests.
Incident
- Workflow: CI run 41637877205, attempt 1,
pull_requestfor #1238 atef2c4470c. Later pushes superseded that head, and the run of the current head,340d4662c, passed. - Failed job:
NVX CLI / Windows, stepValidate NVX CLI. It failed in thepython -m unittestcommand of thevalidate-nvxaction's Windows step (action.yml#L26-L35). Of 1,788 tests, 66 were skipped, and this test was the only failure. - Runner: GitHub-hosted
windows-latest, imagewindows-2025-vs202620261030.294.1, runner 2.338.2, Python 3.12.10, on aStandard_D4ads_v5VM with 4 logical processors (AMD EPYC 7763). - #1238 changes neither the tests nor the code that they test. Both are byte-identical at all six failing heads and on
devat41bc47533.
Decisive output:
test_a_connect_still_in_progress_is_shut_down_at_the_deadline (scripts.test_host_telemetry.AzureMetadataRequestTests.test_a_connect_still_in_progress_is_shut_down_at_the_deadline) ... FAIL
…
======================================================================
FAIL: test_a_connect_still_in_progress_is_shut_down_at_the_deadline (scripts.test_host_telemetry.AzureMetadataRequestTests.test_a_connect_still_in_progress_is_shut_down_at_the_deadline)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\a\nvx\nvx\scripts\test_host_telemetry.py", line 2087, in test_a_connect_still_in_progress_is_shut_down_at_the_deadline
self.assertTrue(blocked.is_set())
AssertionError: False is not true
----------------------------------------------------------------------
Ran 1788 tests in 400.607s
FAILED (failures=1, skipped=66)
The test took about 0.28 s, from the previous test's result line at 12:49:32.914Z to its own at 12:49:33.197Z. That covers its 0.2 s deadline and part of the 0.1 s grace that follows it.
History
I read the logs of every failed NVX CLI job, every attempt, in the CI runs created from 2026-10-15 through 2026-11-03 that failed, were cancelled, or were rerun: 443 of 1,722 runs, with 40 failed NVX CLI jobs, 23 on Windows and 17 on Linux.
- These tests failed 6 of them, all on Windows, and nothing else failed in those 6 jobs.
- In the same runs, 1,335
NVX CLI / Windowsjobs completed, of which 1,312 passed. Of the 1,347NVX CLI / Linuxjobs that completed, 1,330 passed. - The two tests and the code that they test were byte-identical at all six heads.
| Created (UTC) | Run | Event | Head | Job | Test time | Outcome |
|---|---|---|---|---|---|---|
| 2026-10-28 15:56 | 40721083119 | pull_request, #953 |
6b4c3a143 |
122931840730 | 0.20 s | Cancelled; #953 merged later. |
| 2026-10-28 22:58 | 40783994317 | pull_request, #975 |
efda0a83e |
123152677177 | 0.26 s | Not rerun; #975 merged later. |
| 2026-10-29 19:50 | 40943638955 | workflow_dispatch |
0990976a9 |
123693186847 | 0.26 s | Attempt 2 passed. |
| 2026-10-30 20:58 | 41127439071 | pull_request, #1026 |
1dc989912 |
124310135694 | – | The sibling failed too. Not rerun; #1026 merged later. |
| 2026-11-03 00:33 | 41553658419 | merge_group, #1208 |
84151a7d5 |
125647714719 | 0.23 s | Cancelled when the queue replaced the group; #1208 merged from a later group at 01:05. |
| 2026-11-03 12:41 | 41637877205 | pull_request, #1238 |
ef2c4470c |
125924357997 | 0.28 s | This incident. |
Each test time runs from the previous test's result line to the failure's, in the job log.
In job 124310135694, the sibling failed with:
File "D:\a\nvx\nvx\scripts\test_host_telemetry.py", line 472, in test_metadata_request_is_aborted_at_the_deadline
self.assertTrue(aborted.wait(2), "the metadata connection was left open")
AssertionError: False is not true : the metadata connection was left open
Root cause
Permalinks point to dev at 41bc47533.
-
The request has an end-to-end deadline.
read_azure_compute_metadata(host_telemetry.py#L537-L568) runs the request on a daemon worker thread and joins it until the deadline. If the worker is still running at the deadline, it does three things (L559-L565):- it cuts the request off;
- it waits up to
AZURE_IMDS_CUT_OFF_GRACE_SECONDS, 0.1 s (L467), for the worker; - it raises
TimeoutError.
The cut-off shuts down every socket that the request registered and refuses any later one (L470-L491).
Connection.connectregisters its socket before it connects. If the cut-off came first, it closes the socket and raisesTimeoutErrorwithout connecting (L497-L516). -
The main test needs the connect to start before the deadline.
test_a_connect_still_in_progress_is_shut_down_at_the_deadline(test_host_telemetry.py#L2061-L2088) replacessocket.socketwith a class whoseconnectsetsblockedand then waits untilshutdownreleases it. It callsread_azure_compute_metadata(timeout=0.2)and asserts thatblockedandreleasedare set.- If the worker has not created its socket by the deadline, the cut-off has nothing to shut down.
- The worker's late connect is then refused before it reaches the patched
connect. read_azure_compute_metadatastill raises the expectedTimeoutError, so the test fails only atself.assertTrue(blocked.is_set()).
-
The sibling has the same race.
test_metadata_request_is_aborted_at_the_deadline(L443-L482) serves headers from a real socket server, which keeps sending until its connection breaks. The test asserts that a send failed within 2 s. If the worker's connect is refused, the server never accepts a connection, soabortedis never set. The test then fails with the misleading message "the metadata connection was left open". -
A hosted runner can hold the worker up for longer. Locally, the worker reaches
connectin under 0.05 s: with a 0.15 s delay, both tests still pass. All six failures were on the 4-processorwindows-latestrunners, whose stalls also caused #1030. The logs don't show where the worker was held up, because the tests don't report how far it got.
Reproduction
I used a 20-thread Windows 11 Enterprise workstation with Python 3.14.10, and dev's versions of the tests and of host_telemetry.py. No hypervisor is needed.
- Unchanged, the main test passed 100 of 100 runs, in 0.204 s each, and the sibling 100 of 100, in 0.272 s each.
- Delaying the worker decides the outcome. The script below wraps
host_telemetry._fetch_compute_metadata, which the worker calls to open the request, with atime.sleep:
| Worker delay | Main test | Sibling |
|---|---|---|
| 0.15 s | 3 of 3 passed | 3 of 3 passed |
| 0.25 s | 3 of 3 failed with AssertionError: False is not true, in 0.26 s each |
3 of 3 failed with "the metadata connection was left open", in 2.27 s each |
| 0.35 s | 3 of 3 failed the same way, in 0.32 s each | 3 of 3 failed the same way, in 2.34 s each |
Reproduction script
"""Usage: python repro_telemetry.py <nvx-root> <delay-seconds> [iterations]"""
import io
import sys
import time
import unittest
from pathlib import Path
root = Path(sys.argv[1])
delay = float(sys.argv[2])
iterations = int(sys.argv[3]) if len(sys.argv) > 3 else 1
sys.path.insert(0, str(root / "scripts"))
import test_host_telemetry # noqa: E402
from nvx_tools import host_telemetry # noqa: E402
original = host_telemetry._fetch_compute_metadata
def delayed(*args, **kwargs):
# Stands in for a worker that the runner does not schedule for `delay` seconds.
time.sleep(delay)
return original(*args, **kwargs)
if delay > 0:
host_telemetry._fetch_compute_metadata = delayed
for case, name in [
(test_host_telemetry.AzureMetadataRequestTests, "test_a_connect_still_in_progress_is_shut_down_at_the_deadline"),
(test_host_telemetry.AzureProvenanceTests, "test_metadata_request_is_aborted_at_the_deadline"),
]:
failures = 0
for _ in range(iterations):
result = unittest.TextTestRunner(stream=io.StringIO(), verbosity=0).run(case(name))
failures += bool(result.failures or result.errors)
print(f"delay={delay}s {name}: {failures}/{iterations} failed")
Impact
- A failure turns
NVX CLI / WindowsandRequired status checkred. On adevpush, it also skipsRecord performance resultsandPublish development release, which need the job, so that commit gets neither. - On a pull request, the check stays red until someone reruns the job, as in run 40943638955, whose attempt 2 passed.
- In a merge group that the queue does not replace, a failure removes the group's pull requests from the queue. The queue replaced the group of run 41553658419 before that happened.
Suggested fix
- Let the tests, not a 0.2 s timer, decide when the deadline passes. For example, use a clock that the tests control, and let the deadline expire only once the patched
connectis pending. A worker that starts late then still exercises a connect in progress. - Make the sibling assert first that the server accepted a connection, before it asserts that the connection was aborted, so that a refused connect fails with an accurate message.
- Neither raise the 0.2 s alone nor retry. A longer timer only narrows the race, at the cost of slower tests. A retry would hide a cut-off that really misses a pending connect.
Related work
- #523 added both tests, in
021b91651and69f99b3df, and merged on 2026-10-15. - #1030 and #1017 were earlier flakes of short limits on hosted Windows runners.
- #1238's first CI run hit this.
- Dominant language
- Rust
- Stars
- 758
- Forks
- 48
- Avg merge
- 17h 6m
- Merged PRs (30d)
- 452
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/nvx
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
microsoft/nvx#1172 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
microsoft/nvx#1151 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
microsoft/nvx#1147 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
microsoft/nvx#1146 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
microsoft/nvx#1144 · 1 comment ·
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
-
type: documentation
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
tauri-apps/tauri#16476 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
fish-shell/fish-shell#13084 · 1 comment ·
Maintainers usually reply within 2 days
-
bug: merge_insert does not detect concurrent inserts of the same FixedSizeBinary primary keyPossibly taken @lance-gatefixer claimed this today. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
lance-format/lance#10811 ·
Maintainers usually reply within 2 days
-
provider testing
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
mishraprafful/multihull#234 ·
Maintainers usually reply within 1 day