Skip to main content
← Back to list
01Issue
BugClosedSwamp CLI
Assigneeskeeb

Relationships

#1123 flaky test: serve OAuth client tests leak unconsumed fetch response bodies (+ connection_test readDir leak)

Opened by keeb · 7/13/2026

Summary

Running the full suite (deno run test) on main fails with 5 resource-leak sanitizer errors that are unrelated to any product code — they are test-harness leaks. Four are deterministic and reproduce in isolation; one is an order-dependent cross-test readDir leak.

Discovered while verifying the fix for #1121 (a similar OTel timer leak in a workflows test).

Failing tests

Deterministic (fail even when the file is run alone):

  • src/serve/oauth_client_test.ts:129startDeviceGrant: throws on HTTP error
  • src/serve/oauth_client_test.ts:405getUserInfo: throws on HTTP error
  • src/serve/oauth_client_test.ts:445resolveUsername: throws on 404
  • src/serve/oauth_client_test.ts:466resolveUsername: throws on other HTTP error

All four fail with:

Leaks detected:
  - A fetch response body was created during the test, but not consumed during
    the test. Consume or close the response body `ReadableStream`, e.g
    `await resp.text()` or `await [HOST-1]()`.

Order-dependent (observed in the full-suite run, not in isolation):

  • src/serve/connection_test.ts:1618typeArg authz: narrow grant + mismatched typeArg is denied
Leaks detected:
  - An async operation to read a directory was started before the test, but
    completed during the test. Async operations should not complete in a test
    if they were not started in that test. This is often caused by not
    collecting all items in the async iterable returned from a `Deno.readDir`
    call.

Root cause

The OAuth client error paths construct a Response (via fetch) and throw on non-2xx status without consuming or cancelling the response body. Deno's leak sanitizer flags the dangling ReadableStream. The connection_test failure is a separate Deno.readDir iterable that isn't fully drained, surfacing as a cross-test async-op leak.

Impact

deno run test turns red on a clean checkout independent of the change under test, masking real regressions locally and (potentially) in CI. Same class of noise as #1121 and the previously-fixed #645.

Steps to reproduce

# Deterministic (4 failures):
deno test --allow-all src/serve/oauth_client_test.ts

# Full-suite (adds the connection_test readDir leak):
deno run test

Suggested fix

Test-only. In the OAuth client error-handling paths, consume or cancel the response body before throwing (e.g. await resp.body?.cancel() or await resp.text()), or have the test assert against a body-less error. For connection_test.ts, fully drain the Deno.readDir async iterable (or scope the sanitizer with a comment) so the op completes within the test that starts it.

Environment

  • Deno test runner, leak sanitizer on (default under deno run test).
  • Reproduces on a pristine main checkout; found during #1121.
02Bog Flow
OPENTRIAGEDIN PROGRESSCLOSED+ 1 MOREASSIGNEDCLASSIFICATION

Closed

7/13/2026, 10:03:06 PM

No activity in this phase yet.

03Sludge Pulse
keeb assigned keeb7/13/2026, 9:39:50 PM
Editable. Press Enter to edit.

keeb commented 7/13/2026, 10:03:02 PM

Closing as not-reproducing on the supported toolchain.

The reported leak-sanitizer failures only occur on Deno < 2.8. CI pins deno-version: v2.8.x (resolves to 2.8.3), and Deno 2.8 relaxed the sanitizer so an unconsumed fetch response body is no longer flagged as a resource leak.

Verified with the exact failing file:

  • deno 2.7.13 (stale local): deno test --allow-all src/serve/oauth_client_test.ts → 16 passed / 4 failed (leaks)
  • deno 2.8.3 (= CI): same command → 20 passed / 0 failed

So deno run test was never red on the supported Deno; the failures were a stale-local-Deno artifact, not a product or CI regression. Fixed locally by upgrading Deno to match CI.

The underlying nit — cancelling the response body before throwing in the oauth_client.ts error paths — is valid defense-in-depth but low value, so leaving it unfixed.

Sign in to post a ripple.