Skip to content

node:test: assertion failures inside async callback (done-style test) are ignored and test incorrectly passes #28501

Closed

Description

@kevgeoleo

What version of Bun is running?

1.3.11-canary.1+9e93bfa1b

What platform is your computer?

Linux 5.10.0-12-amd64 x86_64 x86_64

What steps can reproduce the bug?

Description

When using callback-style tests (done) with node:test, assertion failures thrown inside asynchronous callbacks (e.g. setTimeout) are not captured by Bun's test runner. As a result, the test is reported as passing even though an assertion fails.

PoC

"use strict";
const { describe, test } = require("node:test");
const assert = require("node:assert");

describe("describe wrapper", () => {
  test("callback test", (t, done) => {
    setTimeout(() => {
      assert.ok(false, "oh no an assert failed");
      done();
    });
  });

});

What is the expected behavior?

root@KContainer:~/51381# node --test b.test.cjs
▶ describe wrapper
  ✖ callback test (3.315869ms)
✖ describe wrapper (4.996912ms)
ℹ tests 1
ℹ suites 1
ℹ pass 0
ℹ fail 1
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 96.535574

✖ failing tests:

test at b.test.cjs:6:3
✖ callback test (3.315869ms)
  AssertionError [ERR_ASSERTION]: oh no an assert failed
      at Timeout._onTimeout (/51381/b.test.cjs:8:14)
      at listOnTimeout (node:internal/timers:605:17)
      at process.processTimers (node:internal/timers:541:7) {
    generatedMessage: false,
    code: 'ERR_ASSERTION',
    actual: false,
    expected: true,
    operator: '==',
    diff: 'simple'
  }

What do you see instead?

root@KContainer:~/51381# bun test b.test.cjs
bun test v1.3.11-canary.1 (9e93bfa1)

b.test.cjs:
✓ describe wrapper > callback test [1.25ms]

 1 pass
 0 fail
Ran 1 test across 1 file. [55.00ms]

Additional information

No response

Activity

  1. robobun commented on Mar 24, 2026

    @robobun
    Collaborator

    ✅ Fix submitted: PR #28502

    Root cause: createTest() in node:test never passed a done callback to callback-style test functions — the test completed synchronously before async callbacks fired.

    Fix: Check fn.length >= 2 and pass a guarded done callback instead of completing immediately. Handles sync throws, async rejections, and double-call protection.

    CI status: Latest build failures are Zig compiler download infrastructure issues (not code-related). Awaiting retrigger.

    Verification (local):

    • USE_SYSTEM_BUN=1 → 2 fail (bug exists)
    • bun bd test → 5 pass (fix works)
  2. added 2 commits that reference this issue on Mar 24, 2026
    29db60d
    a23b675
  3. computer4000 commented on Mar 25, 2026

    @computer4000

    Verified. Bun's test runner passes assertions in async callbacks that should fail.

    Why this matters: Tests with done callbacks that fail in setTimeout/async code will pass silently, hiding real bugs.

    Workaround: Avoid done-style tests. Use async/await pattern:

    // Instead of:
    test("callback test", (t, done) => {
      setTimeout(() => {
        assert.ok(false, "this should fail");
        done();
      });
    });
    
    // Use:
    test("callback test", async () => {
      await assert.rejects(async () => {
        throw new Error("expected");
      });
    });
  4. added a commit that references this issue on Mar 26, 2026
    a3d91ee
  5. added a commit that references this issue on Jun 29, 2026
    65c98c3
  6. robobun commented on Jul 24, 2026

    @robobun
    Collaborator

    Fixed by #32631.

    Verified on main at df84f8d: node:test done-style tests now wait for the callback, so assertion failures inside async callbacks (e.g. setTimeout) correctly fail the test.

    $ bun test repro-28501.cjs
    AssertionError: oh no an assert failed
    (fail) describe wrapper > callback test
     0 pass
     1 fail
    # exit code 1
    

    vs 1498d7b (before fix):

    (pass) describe wrapper > callback test
     1 pass / 0 fail   # exit code 0 — bug
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions