Repository navigation
coverage-v8: conditional await produces a negative implicit-else branch count #11690
Description
Describe the bug
With the V8 coverage provider, a conditionally reached await before an if without an explicit else produces a negative implicit-else branch count. The reduced test exercises both arms and passes, but branch coverage is 75% and LCOV contains BRDA:3,1,1,-2.
Expected: the if branches have counts [4, 1], and all four branches in the file are covered.
Actual: the if branches have counts [4, -2], despite the final assertion proving that the implicit else ran.
This was reduced from cncf/prow-github-actions#511 and reproduced for cncf/prow-github-actions#550. I searched the tracker: #11476 and #11541 concern mixed native/transformed imports, whereas this reproduction has only one source import in one test file. #9447 concerns an actually untested implicit else; this test exercises it.
Reproduction
In an empty directory, run npm init -y, then:
npm install --save-dev vitest@5.0.4 @vitest/coverage-v8@5.0.4 vite@8.3.4
mkdir .repro-550Create these three files:
.repro-550/gate.ts:
export async function gate(a: boolean, b: boolean): Promise<string> {
const ok = a || (await Promise.resolve(b))
if (ok)
return 'yes'
return 'no'
}.repro-550/gate.test.ts:
import { expect, test } from 'vitest'
import { gate } from './gate'
test('both arms execute after short-circuiting and awaiting', async () => {
for (let i = 0; i < 3; i++)
expect(await gate(true, false)).toBe('yes')
expect(await gate(false, true)).toBe('yes')
expect(await gate(false, false)).toBe('no')
}).repro-550/vitest.config.mjs:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
include: ['.repro-550/gate.test.ts'],
coverage: {
provider: 'v8',
include: ['.repro-550/gate.ts'],
reporter: ['text', 'lcovonly', 'json'],
reportsDirectory: '.repro-550/coverage',
},
},
})Run:
npx vitest run --coverage --config .repro-550/vitest.config.mjs
cat .repro-550/coverage/lcov.infoObserved: one passing test; 100% statements/functions/lines, 75% branches.
Relevant LCOV records:
FNDA:5,gate
DA:2,5
DA:3,2
DA:4,4
DA:5,1
BRDA:2,0,0,5
BRDA:2,0,1,2
BRDA:3,1,0,4
BRDA:3,1,1,-2
BRF:4
BRH:3
The counts suggest that the range enclosing the if counts only the two calls that resumed from the conditional await, while the then-block counts all four executions. Subtracting the then-block count from that enclosing count would yield 2 - 4 = -2. This is a hypothesis about the conversion, not a confirmed V8 root cause.
System Info
OS: Linux x64, kernel 7.2.5-200.fc44.x86_64
Node: v24.22.0
V8: 13.6.233.17-node.57
npm: 11.19.1
vitest: 5.0.4
@vitest/coverage-v8: 5.0.4
vite: 8.3.4
ast-v8-to-istanbul: 1.0.7
Used Package Manager
npm
— hive: backend=copilot model=gpt-6.1-sol
🐝 Hive Agent: contributor | SHA: unknown
Activity
AriPerkkio commentedon Oct 21, 2026
This is a V8 bug. Could you file this to them? I've listed couple of more reproduction setups in AriPerkkio/ast-v8-to-istanbul#207.
mrbobbytables commentedon Oct 21, 2026
Thanks! Will do
AriPerkkio commentedon Oct 21, 2026
Great, thanks! Their issue tracker is at https://crbug.com/v8/new. Please post link to the issue here too if possible. 🙏
docs(coverage): link upstream negative-count branch report (#552)
mrbobbytables commentedon Oct 21, 2026
Here is the link: https://issues.chromium.org/issues/576177991