## TL;DR

A promise your test did not await resolved after the test finished, and its `console.log` (or `console.error`) fired into a dead test. Await the promise or return it from the test so Jest waits. If the log comes from background work you genuinely cannot await, silence that logger in setup instead of restructuring the test.

## Error

```text
Cannot log after tests are done. Did you forget to wait for something async in your test?
Attempted to log: "webhook delivered".
```

## Steps

1. Find the test that finished early: look for a missing `await`, a promise created but not returned, or a callback-style API with no done signal. Expected: you spot the fire-and-forget call.
2. Await it: add the missing `await`, `return` the promise from the test, or wrap the callback in a promise. Expected: the log now appears inside the test output and the warning is gone.
3. If the test uses fake timers, advance them so the async work completes before the test ends. Expected: no pending work survives the test.
4. If the log comes from intentional background work (polling, heartbeats), mock the logger for that module in a setup file, for example `jest.spyOn(console, "log").mockImplementation(function () {})` scoped to the noisy module. Expected: the warning is gone and other logs are unaffected.
5. As a diagnostic, run with `--detectOpenHandles`. Expected: Jest names the timer or socket keeping the async work alive.

## When to use

- The exact "Cannot log after tests are done" warning appears in Jest output.
- A `console.log` from your code shows up after its test completed.
- Tests pass but the output carries this warning on every run.

## When not to use

- Nothing logs after tests; the warning names the attempted log, so no log means a different issue.
- The async work must finish for assertions to hold; awaiting is required, and silencing the logger would hide a real bug.
- You are on Vitest or Mocha; this message is Jest-specific.

## Tool compatibility

- Jest 27 through 30 (message wording is stable across these).
- Works with jsdom and node environments.
- `--detectOpenHandles` flag available in all of them.

## Variant phrasings

### jest did you forget to wait for something async

Same warning. Jest detected a console call after the test completed and suspects an un-awaited promise.

### attempted to log after tests are done

The second line of the warning; it shows the message that could not be printed.

## Why it happens

Jest swaps in a per-test `console` and restores it after each test. When an un-awaited promise resolves later and calls `console.log`, the call lands after restoration, so Jest warns instead of printing. The log is a symptom; the un-awaited async work is the disease, and it can also mean assertions silently never ran.

## Edge cases

- Silencing the logger without awaiting can hide assertions that never ran; prefer awaiting, and silence only true background noise.
- `done` callbacks mixed with async functions double-complete tests; pick one style per test.
- The warning can come from third-party libraries with background timers; mock at the library boundary.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_tKHbqZ5A3KtZDaPIpQPMMg
