vitest-dev/vitest · error · Error
Invalid command name "${command}". Only alphanumeric charact
Error message
Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed. What it means
`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.
Source
Thrown at packages/browser/src/node/projectParent.ts:118
}
// some browsers (looking at you, safari) don't report queries in stack traces
// the next best thing is to try the first id that this file resolves to
const files = moduleGraph.getModulesByFile(resolvedPath)
if (files && files.size) {
return files.values().next().value!.id!
}
return id
},
}
for (const [name, command] of Object.entries(builtinCommands)) {
this.commands[name] ??= command
}
// validate names because they can't be used as identifiers
for (const command in this.config.browser.commands) {
if (!/^[a-z_$][\w$]*$/i.test(command)) {
throw new Error(
`Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`,
)
}
this.commands[command] = this.config.browser.commands[command]
}
this.prefixTesterUrl = `${base || '/'}`
this.prefixOrchestratorUrl = `${base}__vitest_test__/`
this.faviconUrl = `${base}__vitest__/favicon.svg`
this.manifest = (async () => {
return JSON.parse(
await readFile(`${distRoot}/client/.vite/manifest.json`, 'utf8'),
)
})().then(manifest => (this.manifest = manifest))
this.orchestratorHtml = (this.config.browser.ui
? readFile(resolve(uiClientRoot, 'index.html'), 'utf8')View on GitHub (pinned to d568f8ce37)
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.
Example fix
// before
export default defineConfig({
test: {
browser: {
commands: { 'login-as': ({ page }) => page.fill('#user', 'x') },
},
},
})
// after
export default defineConfig({
test: {
browser: {
commands: { loginAs: ({ page }) => page.fill('#user', 'x') },
},
},
}) Defensive patterns
Strategy: validation
Validate before calling
function validateCommandConfig(commands: Record<string, unknown>): void {
for (const name of Object.keys(commands)) {
if (!/^[a-z_$][\w$]*$/i.test(name)) {
throw new Error(`browser.commands key "${name}" is not a valid identifier`)
}
}
} Type guard
const hasValidCommandKeys = (commands: Record<string, unknown>): boolean => Object.keys(commands).every(n => /^[a-z_$][\w$]*$/i.test(n))
Prevention
- 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.
When it happens
Trigger: Configuring `browser.commands` with at least one invalid key, e.g. `{ 'my-command': ({ page }) => ... }` or `{ '1action': fn }`.
Common situations: 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.
Related errors
- `resolveOptions` has to be used in a test file
- Invalid command name "${name}". Only alphanumeric characters
- Cannot compare screenshots without a test path
- Provider ${this.provider.name} does not support command "${n
- Commands are only available for browser tests.
AI-assisted analysis of vitest-dev/vitest@d568f8ce37 (2026-08-03).
Data as JSON: /data/errors/650b9e50061a8112.json.
Report an issue: GitHub.