bug(agent): test_server.py leaks live pipeline threads — reset fixture clears _active_threads without joining, causing cross-test mock contamination
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 72/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- python
- Domain
- testing-qa
Research direction
Start in agent/tests/test_server.py at reset_server_state and the existing test_background_thread_failure_503_and_backup_terminal_write pattern; inspect server.py:_active_threads and _spawn_background for the lifecycle being tested. Run the relevant test_server.py tests, then add coverage for a slow background task and verify fixture setup and teardown wait for live threads and fail on timeout without contaminating the next test.
Written by the indexing model from the issue text.
Description
Problem
agent/tests/test_server.py intermittently fails with a mock being called by a test that never made the call. Observed twice on PR #680 (a CLI-only PR that cannot touch agent/), each time on a different test:
| Run | Test |
|---|---|
| 33446493920 | TestMicrovmRunHookPlatformConfig::test_no_platform_config_and_no_baked_env_is_refused_not_run_unscoped |
| earlier run | TestMicrovmRunHookPreInstallAwsSilence::test_no_cloudwatch_or_credential_seam_is_touched_before_the_install[no-config] |
FAILED tests/test_server.py::TestMicrovmRunHookPlatformConfig::test_no_platform_config_and_no_baked_env_is_refused_not_run_unscoped
AssertionError: Expected 'mock' to not have been called. Called 1 times.
Calls: [call(repo_url='org/repo', task_description='do it', github_token='ghp_x',
anthropic_model='', aws_region='', task_id='t-pc', max_turns=100, ...)]
main is green (verified by manual workflow_dispatch 33510225606: agent 1755 passed, cdk 4322 passed, cli 768 passed), so this is a race, not a regression — a single green run does not clear it.
Root cause
Three facts combine:
-
run_taskis resolved at call time, in a background thread.server.py:35doesfrom pipeline import run_task;_run_task_backgroundcalls it bare atserver.py:478. A bare global resolves when executed, not when the thread starts. That function is thethreading.Threadtarget in_spawn_background(server.py:736,target=at:748). -
The reset fixture drops thread references without joining them.
test_server.py:21-29:@pytest.fixture(autouse=True) def reset_server_state(): ... with server._threads_lock: server._active_threads.clear() # <-- drops the handle; thread keeps running_active_threads(server.py:255) is only a tracking list. Clearing it does not stop anything. -
Every test in
TestMicrovmRunHookPlatformConfigsharestask_id: "t-pc"(the_payload()default attest_server.py:2364), and six of them return200, each spawning a realpipeline-t-pcthread whilemonkeypatch.setattr(server, "run_task", MagicMock())is in effect.
The sequence: an accepting test spawns pipeline-t-pc → the fixture clears the list and pytest advances → the still-live thread reaches server.py:478, looks up server.run_task, and receives the next test's mock → that test's assert_not_called() fails.
The captured call args confirm it rather than merely fitting it: task_id='t-pc' is the class-wide default that the refusal test would never start, and aws_region='' / anthropic_model='' are env-derived values already stripped — a sibling test's environment, not this one's. Note also that the refusal test's own asserts (400, MICROVM_RUN_PLATFORM_CONFIG_INCOMPLETE, missing_env) all passed; only assert_not_called() failed. The request path is correct. The contamination is external.
Why it surfaced now
Not new code — new volume. MicroVM P1 (#645 / f3cfb4e3b, Aug 6) and P2 (#733 / 4d53a73b0, Aug 28) added several accepting /run hook tests that all share one task_id, widening the window. build (agentcore) has no push: trigger (pull_request, merge_group, workflow_dispatch only) and last ran on main on 2026-06-04, so nothing re-verifies main between merges either — filing separately.
Same family as #615 (scoped-session bypassing a boto3.client mock): a per-test mock defeated by state the fixture does not actually reset. #615 fixed the env half by adding AGENT_SESSION_ROLE_ARN to _AGENT_ENV_VARS (conftest.py:162); the thread half is still open.
Fix
The file already contains the correct pattern — test_background_thread_failure_503_and_backup_terminal_write (test_server.py:60-76) polls until no thread is_alive(), with a comment explaining that joining "eliminates the race." Promote that into the autouse fixture.
-
reset_server_statejoins (with timeout) every live thread in_active_threadsbefore clearing, on both setup and teardown - Fail loudly if a thread outlives its timeout, rather than clearing and continuing — a silent leak is what produced this
- Give each accepting test in
TestMicrovmRunHookPlatformConfiga distincttask_idso a leak is attributable to its origin instead of anonymous - Regression test: spawn a deliberately slow
run_task, let the fixture tear down, assert the next test's mock is untouched (should fail before the fix) - Re-run #680's build once merged and confirm it goes green with no code change to that PR
Not in scope
_spawn_background's production behaviour is fine — daemon-free named threads tracked in a list is reasonable for the runtime. This is purely test lifecycle.
- Dominant language
- TypeScript
- Stars
- 158
- Forks
- 49
- Avg merge
- 3d 7h
- Merged PRs (30d)
- 17
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
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 aws-samples/sample-autonomous-cloud-coding-agents
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
aws-samples/sample-autonomous-cloud-coding-agents#950 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
aws-samples/sample-autonomous-cloud-coding-agents#908 ·
Maintainers usually reply within 1 day
-
bug v1
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
aws-samples/sample-autonomous-cloud-coding-agents#886 ·
Maintainers usually reply within 1 day
-
bug v1
Difficulty 2/5 1-3 hours Newbie friendliness 80/100
aws-samples/sample-autonomous-cloud-coding-agents#861 ·
Maintainers usually reply within 1 day
-
documentation P2 security
Difficulty 2/5 1-2 days Newbie friendliness 74/100
aws-samples/sample-autonomous-cloud-coding-agents#793 ·
Maintainers usually reply within 1 day
All issues in aws-samples/sample-autonomous-cloud-coding-agents
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
microsoft/playwright#43327 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
microsoft/TypeScript#64758 ·
Maintainers usually reply within 1 day
-
enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
opensearch-project/OpenSearch-Dashboards#12937 ·
Maintainers usually reply within 2 days
-
Liveness marks block-included attesters live in the block's epoch instead of the attestation's epochOpen
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
Maintainers usually reply within 1 day
-
status: needs-triage
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
a2ui-project/a2ui#3158 ·
Maintainers usually reply within 2 days