Skip to content

Vitest extension doesn't run test files with tags #799

Description

@bennycode

Describe the bug

I am using tags in my unit tests and noticed that the Vitest extension for Visual Studio Code cannot run tests that are having tags declared.

Screenshot:

Image

Expected behavior

Tests are being executed.

Reproduction

Build a test case with tags, i.e.

describe('getResultOrThrow', () => {
  it('identifies an up-trending asset by a positive ROC', {tags: ['tulipindicators']}, () => {
    // ...  
  });
});

The extension cannot run the test although the tags are defined in the vites.config.ts file:

import { defineConfig, mergeConfig } from "vitest/config";
import baseConfig from "../../vitest.config.base";

export default mergeConfig(
  baseConfig,
  defineConfig({
    test: {
      tags: [
        {
          description: "...",
          name: "tulipindicators",
        },
      ],
    },
  }),
);

My vitest.config.base.ts file looks like this:

import {defineConfig} from 'vitest/config';

export default defineConfig({
  esbuild: {
    /*
     * Allows using the "accessor" keyword in TypeScript:
     * https://github.com/vitest-dev/vitest/issues/5976#issuecomment-2190804966
     */
    target: 'es2022',
  },
  test: {
    bail: 1,
    coverage: {
      exclude: ['src/start/**'],
      include: ['src/**'],
      provider: 'v8',
      reporter: ['html', 'lcov', 'text'],
    },
    environment: 'node',
    globals: true,
    // https://main.vitest.dev/guide/learn/writing-tests-with-ai.html#mock-cleanup
    restoreMocks: true,
    sequence: {
      concurrent: true,
      shuffle: true,
    },
  },
});

Output

> Error: The Vitest config does't define any "tags", cannot apply "tulipindicators" tag for this test. See: https://vitest.dev/guide/test-tags

Extension Version

1.50.8

Vitest Version

4.1.10

Validations

Activity

  1. bennycode commented on Aug 3, 2026

    @bennycode
    Author

    I debugged this by replicating the extension's worker code path (packages/worker/src/index.ts → createVitest + packages/worker/src/runner.ts → rerunTestSpecifications) in a standalone Node script and found the root cause.

    TL;DR: The extension's config discovery treats my shared base config as a runnable config and spawns a second Vitest instance for it and that instance has no tags.

    Root cause

    1. The extension discovers configs via this glob:
      // packages/extension/src/constants.ts
      export const configGlob = '**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}'
      My shared base file vitest.config.base.ts matches this pattern.
    2. resolveVitestConfigs() in packages/extension/src/spawn/pkg.ts spawns one Vitest instance per matched file (intentionally, to support vitest.e2e.config.ts etc.).
    3. The base-config instance is rooted at the repo root with the default include, so it claims the same test files as my package-level configs — but the base config defines no tags.
    4. When that instance collects a tagged test, @vitest/runner's validateTags throws exactly the reported error.

    The CLI never fails because nothing ever runs vitest --config vitest.config.base.ts.

    Minimal reproduction

    Three files (this is the structure of my real project, trading-signals):

    vitest.config.base.ts              # shared options, no tags
    packages/foo/vitest.config.ts      # mergeConfig(base, { test: { tags: [...] } })
    packages/foo/test/tags.test.ts     # it('...', { tags: ['tulipindicators'] }, ...)
    

    Creating a Vitest instance for vitest.config.base.ts (as the extension does) and running the test file reproduces it verbatim on Vitest 4.1.10:

    FAIL packages/foo/test/tags.test.ts
    Error: The Vitest config does't define any "tags", cannot apply "tulipindicators" tag for this test.
    

    Running the same file through the leaf config instance passes. I verified both paths with the exact createVitest options the extension worker uses.

    Note: the sample added in #800 can't reproduce the issue as it defines the tags in the same config file that the extension discovers, which works fine. The repro sample needs the base-config structure above.

    Workaround

    Rename vitest.config.base.ts to vitest.shared.ts (the naming convention from the Vitest docs here) or set vitest.configSearchPatternExclude in VS Code settings.

    Possible fixes on the extension side

    When multiple discovered instances claim the same test file, prefer the config that is closest to the file (dedupe by ownership).

  2. sheremet-va commented on Aug 3, 2026

    @sheremet-va
    Member

    When multiple discovered instances claim the same test file, prefer the config that is closest to the file (dedupe by ownership).

    This is not how the relationship works. It's not that test files define what config they are in, it's the config that defines what test files are in. It is perfectly valid to reuse the same test file in different configs, in my opinion. You can also see what configs provide test files in your UI:

    Image

    You can disable them too with "Toggle Configs" command:

    Image Image

    I am open to improving the error message though.

  3. bennycode commented on Aug 4, 2026

    @bennycode
    Author

    Fair point on the model. But can you help me understand this:

    Why does the extension fail to run my tests when the shared config is named vitest.config.base.ts, but succeed when the exact same file is named vitest.shared.ts?

    I could fix the extension error by just renaming my config: bennycode/trading-signals#1276

  4. sheremet-va commented on Aug 4, 2026

    @sheremet-va
    Member

    Why does the extension fail to run my tests when the shared config is named vitest.config.base.ts, but succeed when the exact same file is named vitest.shared.ts?

    Configs define test profiles. You can see the profiles on the first picture I sent in my previous comment. Profile defines how tests run, not what tests exist. For some reason, in your case the default profile for that test file is vitest.config.base.ts and not the other one, but the extension does creates multiple profiles in your case correctly. You can change the default profile by clicking on the chevron button next to the "run" button.

    vitest.config.base.ts matches the config pattern and vitest.shared.ts does not match the pattern, so Vitest doesn't create a separate profile for it.

  5. bennycode commented on Aug 4, 2026

    @bennycode
    Author

    @sheremet-va thanks for pointing me to the Vitest profiles. I still believe there's an issue with which profile gets selected when rerunning tests from the extension. Since it's tricky to describe in writing, I recorded a short video demonstrating it: https://youtu.be/7ZJjfOY6kb8

  6. sheremet-va commented on Aug 5, 2026

    @sheremet-va
    Member

    @sheremet-va thanks for pointing me to the Vitest profiles. I still believe there's an issue with which profile gets selected when rerunning tests from the extension. Since it's tricky to describe in writing, I recorded a short video demonstrating it: https://youtu.be/7ZJjfOY6kb8

    I already explained everything that you show in your video though. If you don't want your shared config to run the tests, disable the profile or make it non default. I actually don't know why it decides the shared be the default in the gutter icon, probably because of the order (the shared profile is first in the list)

  7. bennycode commented on Aug 5, 2026

    @bennycode
    Author

    I actually don't know why it decides the shared be the default in the gutter icon

    That's exactly my point. When I hit "Run" on a test in /packages/module/my.test.ts from the grid, I expect it to use /packages/module/vitest.config.ts (most specific) instead of resolving to /vitest.config.base.ts.

    TypeScript works the same way: open /packages/module/code.ts and you get errors based on /packages/module/tsconfig.json, even if it extends /tsconfig.base.json.

    Where I see room for improvement is the resolution order. Going by specificity would feel more predictable here. Don't you think so?

  8. sheremet-va commented on Aug 5, 2026

    @sheremet-va
    Member

    Where I see room for improvement is the resolution order. Going by specificity would feel more predictable here. Don't you think so?

    1. I do not think so because it is not general enough. Say, you have multiple configs in the folder. What do you choose now? We just provide the default. If you have a custom setup, configure it.
    2. The extension doesn't pick a favorite, vscode API controls which provider runs. Profile doesn't even know which config it belongs to, you can have multiple profiles for the same config (coverage+debug+run). What we could do is maybe register them in a different order, so vscode picks up the earliest automatically? In that case we would have to delay it until all configs are resolved which would slow down the initialisation for nested configs.

    This is how profiles are defined:

    this.api = await resolveVitestAPI(

    export async function resolveVitestAPI(

  9. added a commit that references this issue on Aug 5, 2026
    b49e19e
  10. bennycode commented on Aug 6, 2026

    @bennycode
    Author

    What we could do is maybe register them in a different order, so vscode picks up the earliest automatically?

    I like that idea, because it helps the user without asking them to understand the extension's internals first.

    To see it from the end user's point of view, this is what happened to me:

    1. I added tags to my test file
    2. I listed the tags properly in my Vitest config
    3. I hit "Run Test" in the grid and got: The Vitest config does't define any "tags"

    This made me totally confused. There was no indicator that I had to switch my Vitest config profile (such a warning or note could also help). Without opening this thread and talking to you, I would have just thought there is a defect with the extension.

  11. added a commit that references this issue on Aug 6, 2026
    bd3dc7b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions