{"id":"650b9e50061a8112","repo":"vitest-dev/vitest","slug":"invalid-command-name-command-only-alphanumer","errorCode":null,"errorMessage":"Invalid command name \"${command}\". Only alphanumeric characters, $ and _ are allowed.","messagePattern":"Invalid command name \"(.+?)\"\\. Only alphanumeric characters, \\$ and _ are allowed\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/browser/src/node/projectParent.ts","lineNumber":118,"sourceCode":"        }\n        // some browsers (looking at you, safari) don't report queries in stack traces\n        // the next best thing is to try the first id that this file resolves to\n        const files = moduleGraph.getModulesByFile(resolvedPath)\n        if (files && files.size) {\n          return files.values().next().value!.id!\n        }\n        return id\n      },\n    }\n\n    for (const [name, command] of Object.entries(builtinCommands)) {\n      this.commands[name] ??= command\n    }\n\n    // validate names because they can't be used as identifiers\n    for (const command in this.config.browser.commands) {\n      if (!/^[a-z_$][\\w$]*$/i.test(command)) {\n        throw new Error(\n          `Invalid command name \"${command}\". Only alphanumeric characters, $ and _ are allowed.`,\n        )\n      }\n      this.commands[command] = this.config.browser.commands[command]\n    }\n\n    this.prefixTesterUrl = `${base || '/'}`\n    this.prefixOrchestratorUrl = `${base}__vitest_test__/`\n    this.faviconUrl = `${base}__vitest__/favicon.svg`\n\n    this.manifest = (async () => {\n      return JSON.parse(\n        await readFile(`${distRoot}/client/.vite/manifest.json`, 'utf8'),\n      )\n    })().then(manifest => (this.manifest = manifest))\n\n    this.orchestratorHtml = (this.config.browser.ui\n      ? readFile(resolve(uiClientRoot, 'index.html'), 'utf8')","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/vitest-dev/vitest/blob/d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09/packages/browser/src/node/projectParent.ts#L100-L136","documentation":"`ParentBrowserProject` constructor iterates over every key in `config.browser.commands` and validates each matches `/^[a-z_$][\\w$]*$/i`. Because command names become JS identifiers in the generated context module, non-identifier names (kebab-case, dotted, leading digit) are rejected at construction time before the server starts.","triggerScenarios":"Configuring `browser.commands` with at least one invalid key, e.g. `{ 'my-command': ({ page }) => ... }` or `{ '1action': fn }`.","commonSituations":"Migrating from a convention that used kebab-case; copy-pasted command names from docs of another framework; programmatic config generation that joins tokens with dashes.","solutions":["Rename the config keys to valid identifiers — `myCommand` instead of `my-command`.","If you must keep a UI-facing name, keep the config key valid and alias inside the implementation.","Run `vitest --config` validation early to catch this before the server boots."],"exampleFix":"// before\nexport default defineConfig({\n  test: {\n    browser: {\n      commands: { 'login-as': ({ page }) => page.fill('#user', 'x') },\n    },\n  },\n})\n\n// after\nexport default defineConfig({\n  test: {\n    browser: {\n      commands: { loginAs: ({ page }) => page.fill('#user', 'x') },\n    },\n  },\n})","handlingStrategy":"validation","validationCode":"function validateCommandConfig(commands: Record<string, unknown>): void {\n  for (const name of Object.keys(commands)) {\n    if (!/^[a-z_$][\\w$]*$/i.test(name)) {\n      throw new Error(`browser.commands key \"${name}\" is not a valid identifier`)\n    }\n  }\n}","typeGuard":"const hasValidCommandKeys = (commands: Record<string, unknown>): boolean =>\n  Object.keys(commands).every(n => /^[a-z_$][\\w$]*$/i.test(n))","tryCatchPattern":null,"preventionTips":["Use valid JS identifiers for browser.commands config keys.","Validate the config object before passing it to defineConfig in tests.","Codemod kebab-case names to camelCase during framework migrations."],"tags":["validation","configuration","browser-command"],"analyzedSha":"d568f8ce3739b532d5bf2c1ee1e45e8a8a473d09","analyzedAt":"2026-08-03T20:23:56.861Z","schemaVersion":2}