firebase / firebase/firebase-tools

Auth emulator stops serving all requests partway through a long test run and never recovers

Open
#10,896 1 comment 0 reactions 0 assignees View on GitHub
emulators: auth type: bug
Dominant language
TypeScript
Stars
4.5k
Forks
1.3k
Avg merge
1d 12h
Merged PRs (30d)
84

Description

### [REQUIRED] Environment info

**firebase-tools:** reproduced on both `14.8.0` and `15.25.1`

**Platform:** Windows 11 Pro (10.0.26200), Node v24.16.0

**Emulators running:** `auth`, `firestore`, `functions` (started with `firebase emulators:start --only auth,firestore,functions`)

### [REQUIRED] Test case

The Auth emulator stops serving requests partway through a long Playwright run and never recovers.

The trigger needs browser traffic against the emulator, so I have not managed to reduce it to a script. What I can give is a precise characterisation and several experiments that narrow it down considerably.

Setup: an Angular app under test, with Playwright routing every `identitytoolkit.googleapis.com` and `securetoken.googleapis.com` request into the Auth emulator via `page.route(...)` / `route.fetch(...)`. Roughly 120 tests run serially with one worker, and each `beforeEach` clears both emulators (`DELETE /emulator/v1/projects/{project}/accounts` and the equivalent Firestore endpoint).

### [REQUIRED] Steps to reproduce

1. Start the emulators as above.
2. Run a serial suite where each test signs in / creates accounts through the emulator via a browser, and clears accounts between tests.
3. Watch the duration of each account-clear call.

### [REQUIRED] Expected behavior

The Auth emulator keeps answering for the life of the run.

### [REQUIRED] Actual behavior

After a fixed number of clears, the Auth emulator stops responding to **every** request and never recovers for the remainder of the process. The Firestore emulator, in the same process, is unaffected throughout.

Timings for the account-clear call across one run on `15.25.1` (calls 1–62 elided, all in this range):

```
call 1: 7ms
call 20: 3ms
call 40: 4ms
call 62: 7ms
call 63: 15007ms <- bound I imposed; without it, it never returns
call 64: 15003ms
call 65: 15015ms
... (every subsequent call, to the end of the run)
```

Every test after that point fails in `beforeEach`.

**The wedge point is deterministic.** Call 63 on `15.25.1`, reproduced identically across three separate runs. Calls 28–29 on `14.8.0`. So the upgrade moves the threshold but does not remove it.

### What I have ruled out

**It is not the client.** I first used `fetch` against the REST endpoint, then switched to the Admin SDK (`listUsers` + `deleteUsers`), a completely different HTTP path. Both wedge at the same point in the run. The Admin SDK has no timeout of its own, so it simply never settles.

**It is not connection-scoped.** During a run I polled `GET /emulator/v1/projects/{project}/config` every 5 seconds from a **separate process**, with a fresh connection each time — read-only, no mutation. It returned 200 in ~230ms throughout, and then at the exact moment the suite's clears began hanging it started timing out at 20s and never recovered:

```
14:47:27 probe 200 0.236472
14:47:52 probe 000 20.015710
14:48:17 probe 000 20.003287
14:48:42 probe 000 20.003797
... (never recovers)
```

An unrelated process, on a new socket, doing a read-only GET, is refused. That points at the emulator's request handling rather than anything on the client side.

**It is not call volume alone.** Against a freshly started emulator with no browser and no app involved, this ran clean:

```js
// 200 cycles: create two accounts, list all, delete all
for (let i = 1; i <= 200; i++) {
await auth.createUser({ email: `probe${i}a@test.com`, password: 'password123' });
await auth.createUser({ email: `probe${i}b@test.com`, password: 'password123' });
const uids = [];
let token;
do {
const page = await auth.listUsers(1000, token);
uids.push(...page.users.map(u => u.uid));
token = page.pageToken;
} while (token);
if (uids.length) await auth.deleteUsers(uids);
}
// 200/200 cycles, 4–7ms each, no wedge
```

So the trigger involves the browser-side traffic, not the clearing itself. My guess, unverified, is that requests proxied in from the browser and abandoned when a page closes leave something in a state the emulator does not recover from, and that the threshold is a connection or handle limit rather than a count of operations.

### Impact and workaround

While it lasts, the emulator makes an otherwise healthy suite report 10–14 failures per run, all of them bare `timeout in beforeEach` on tests that have nothing wrong with them. It is very easy to misread as flaky tests.

The workaround is to shard the suite into separate `playwright test` invocations so each gets a fresh emulator and no single run approaches the threshold. With that, the same suite passes 122/122.

Happy to run further diagnostics if there is a debug flag or log that would help pin down what is holding the emulator open.

Contributor guide

Open the contributing guide

Research direction

Start by running the serial Playwright suite with `firebase emulators:start --only auth,firestore,functions`, using the routed `identitytoolkit.googleapis.com` and `securetoken.googleapis.com` traffic described in the report. Monitor the Auth emulator's account-clear calls and separate `/emulator/v1/projects/{project}/config` probe through the failure threshold. Done means identifying why browser traffic wedges Auth requests and confirming the emulator remains responsive through the full suite.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, node.js, playwright, typescript
Domain
authentication, backend, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.