Skip to content

fix(browser): fix error stacktrace location off-by-one#10724

Merged
hi-ogawa merged 10 commits into
vitest-dev:mainfrom
hi-ogawa:fix/browser-stack-column-offbyone
Jul 14, 2026
Merged

fix(browser): fix error stacktrace location off-by-one#10724
hi-ogawa merged 10 commits into
vitest-dev:mainfrom
hi-ogawa:fix/browser-stack-column-offbyone

Conversation

@hi-ogawa

@hi-ogawa hi-ogawa commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Description

While digging through #10723 (comment), apparently our parseStacktrace + getSourceMap is the one with off-by-one column issue.

Note that we do this combination of parseStacktrace + getSourceMap only for browser mode error mapping on the server and for external module error, so it may not be too surprising to discover this just now.

For example,

  • 🙅 before
$ VITE_FAIL_TEST=1 pnpm -C examples/lit test --browser.headless
...

  test/basic.test.ts:19:72
     17|     await expect.element(page.getByRole('button')).toMatchTextContent('2')
     18|     if (import.meta.env.VITE_FAIL_TEST) {
     19|       await expect.element(page.getByRole('button'), { timeout: 3000 }).toMatchTextContent('3')
       |                                                                        ^
     20|     }
     21|   })

Caused by: Error: Matcher did not succeed in time.
  test/basic.test.ts:19:6
  • ✅ after
$ VITE_FAIL_TEST=1 pnpm -C examples/lit test --browser.headless
...

  test/basic.test.ts:19:73
     17|     await expect.element(page.getByRole('button')).toMatchTextContent('2')
     18|     if (import.meta.env.VITE_FAIL_TEST) {
     19|       await expect.element(page.getByRole('button'), { timeout: 3000 }).toMatchTextContent('3')
       |                                                                         ^
     20|     }
     21|   })

Caused by: Error: Matcher did not succeed in time.
  test/basic.test.ts:19:7

After this change, many old branches of isRolldown / webkit converges with existing chrome/firefox column location, so test snapshots are unified. There are still remaining divergence for webkit but they look mostly cosmetic.

📔 The summary of remaining differences

Helper calls

  await myEqualAsync('async', 'x')
  ^     ^
  C     Firefox/WebKit
  3     9

Chromium points at await; Firefox/WebKit point at the helper call.

Thrown errors

  throw new Error('error')
        ^   ^
        C/F WebKit
         9   13

Chromium/Firefox point at new; WebKit points at Error.

This pattern appears in:

Explicit stack

  const error = new Error('Custom error for stack trace')
                ^   ^
                C/F WebKit
                17   21

trace-mark/basic.test.ts:50

Unhandled error

    throw new Error('custom_unhandled_error')
          ^   ^
          all WebKit-only extra frame
          11  15

WebKit reports both new and Error; other browsers report only new.

throw-unhandled-error.test.ts:9

Bundled dependency

  throw new Error('error from b')
        ^        ^
        C/F      WebKit
         9        18

bundled-lib/src/b.js:2

  return a() + b()
               ^^
               |└─ WebKit: 17, opening parenthesis
               └── C/F:    16, function name

bundled-lib/src/index.js:5

So the remaining difference is mostly semantic placement: Chromium/Firefox tend to identify the expression start, while WebKit often identifies the constructor identifier or call parenthesis.

TODO

Please don't delete this checklist! Before submitting the PR, please make sure you do the following:

  • It's really useful if your PR references an issue where it is discussed ahead of time. If the feature is substantial or introduces breaking changes without a discussion, PR might be closed.
  • Ideally, include a test that fails without this PR but passes with it.
  • Please, don't make changes to pnpm-lock.yaml unless you introduce a new test example.
  • Please check Allow edits by maintainers to make review process faster. Note that this option is not available for repositories that are owned by Github organizations.

Tests

  • Run the tests with pnpm test:ci.

Documentation

  • If you introduce new functionality, document it. You can run documentation with pnpm run docs command.

Changesets

  • Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with feat:, fix:, perf:, docs:, or chore:.

…e map lookup

`parseStacktrace` fed the browser's 1-based `error.stack` column straight
into the source map lookup (0-based), then returned the mapped column
without converting back. The off-by-one was normally masked by wide
mapping segments, but oxc now emits a segment boundary for closing
delimiters, so Safari (which anchors call frames at the open paren) could
cross into the adjacent segment and drift, causing webkit-only column
differences.

Subtract 1 before the lookup and add 1 to the result, matching vite's SSR
stack rewriter (`ssrStacktrace.ts`).

Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
@hi-ogawa hi-ogawa changed the title fix(utils): convert 1-based stacktrace column to 0-based before sourc… fix(utils): fix wrong column with parseStacktrace + getSourceMap Jul 7, 2026
@netlify

netlify Bot commented Jul 7, 2026

Copy link
Copy Markdown

Deploy Preview for vitest-dev ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit eb00674
🔍 Latest deploy log https://app.netlify.com/projects/vitest-dev/deploys/6a55e5a329830200084c2d5f
😎 Deploy Preview https://deploy-preview-10724--vitest-dev.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

hi-ogawa and others added 7 commits July 7, 2026 15:39
Re-baseline the non-browser suites affected by the parseStacktrace column
fix (+1 on source-map-remapped stack columns): e2e stacktraces/reporters/
aria snapshots, coverage source-maps, and the UI trace editor cursor.

Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
Re-baseline browser spec expectations after the parseStacktrace 1-based
to 0-based column fix. Columns now resolve to the correct 1-based source
position, so chromium/firefox shift by +1 and webkit converges onto the
same source column across browsers.

Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
After the stacktrace column fix, several browser-specific expectations
resolve to the same source column. Collapse the webkit/else branches in
playwright-trace-mark and remove the duplicated safari branch in the
"logs have stack traces" runner test.

Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
The non-rolldown webkit branches (nested under `if (rolldownVersion)`)
were not exercised by the rolldown-vite run used to re-baseline, so they
kept pre-fix columns and would fail the vite@7 CI job. Verified against a
local `vite@7` (non-rolldown) run: they converge to the same source
column as the rolldown/other-browser branches.

Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
With the stacktrace column fix, the rolldown and non-rolldown stack
columns resolve to the same source position, so the `if (rolldownVersion)`
branches in aria-snapshot, errors, playwright-trace-mark and the runner
timeout-hooks test are now redundant. Collapse them into a single
snapshot each and drop the unused `rolldownVersion` imports. Verified on
both rolldown-vite and vite@7 (non-rolldown) runs.

Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
Comment on lines +264 to +271
if (stack.line <= 0 || stack.column <= 0) {
return stack
}
const position = getOriginalPosition(traceMap, {
line: stack.line,
// stacktrace's column is 1-indexed, but sourcemap's one is 0-indexed
column: stack.column - 1,
})

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread test/coverage-test/test/source-maps.test.ts
@hi-ogawa
hi-ogawa requested a review from AriPerkkio July 14, 2026 08:13
@AriPerkkio

Copy link
Copy Markdown
Member

I'd recommend to merge this with title fix(browser): error stacktrace location off-by-one instead of fix(utils).

@hi-ogawa hi-ogawa changed the title fix(utils): fix wrong column with parseStacktrace + getSourceMap fix(browser): fix error stacktrace location off-by-one Jul 14, 2026
@hi-ogawa
hi-ogawa merged commit 62b8d3b into vitest-dev:main Jul 14, 2026
26 of 27 checks passed
@hi-ogawa
hi-ogawa deleted the fix/browser-stack-column-offbyone branch July 14, 2026 08:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants