wavetermdev/waveterm · error
invalid environment variable name: %q
Error message
invalid environment variable name: %q
What it means
BuildShellCommand renders a CommandSpec into a single 'sh -c' command string, inlining environment variables as KEY=value assignments. Before inlining, each env var key is validated with isValidEnvVarName (regex ^[a-zA-Z_][a-zA-Z0-9_]*$); this error is thrown for any key that does not match, because such a key would produce a broken or injection-prone shell assignment. The command is not built or run.
Source
Thrown at pkg/genconn/genconn.go:142
return nil, fmt.Errorf("failed to get stdout pipe: %w", err)
}
return syncbuf.MakeSyncBufferFromReader(stdout), nil
}
func MakeStderrSyncBuffer(proc ShellProcessController) (*syncbuf.SyncBuffer, error) {
stderr, err := proc.StderrPipe()
if err != nil {
return nil, fmt.Errorf("failed to get stderr pipe: %w", err)
}
return syncbuf.MakeSyncBufferFromReader(stderr), nil
}
func BuildShellCommand(opts CommandSpec) (string, error) {
// Build environment variables
var envVars strings.Builder
for key, value := range opts.Env {
if !isValidEnvVarName(key) {
return "", fmt.Errorf("invalid environment variable name: %q", key)
}
envVars.WriteString(fmt.Sprintf("%s=%s ", key, shellutil.HardQuote(value)))
}
// Build the command
shellCmd := opts.Cmd
if opts.Cwd != "" {
shellCmd = fmt.Sprintf("cd %s && %s", shellutil.HardQuote(opts.Cwd), shellCmd)
}
// Quote the command for `sh -c`
return fmt.Sprintf("sh -c %s", shellutil.HardQuote(envVars.String()+shellCmd)), nil
}
func isValidEnvVarName(name string) bool {
validEnvVarName := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
return validEnvVarName.MatchString(name)
}View on GitHub (pinned to a4447c1563)
Solutions
- Sanitize CommandSpec.Env keys: keep only those matching ^[a-zA-Z_][a-zA-Z0-9_]*$ and drop or rename the rest before calling.
- If you have raw 'K=V' strings, split them into map entries instead of using 'K=V' as the key.
- For keys like 'ProgramFiles(x86)' that legitimately cannot be passed this way, drop them or export them inside Cmd itself (e.g. via export statements in the command string).
- Log the offending key (it is quoted with %q) and fix it at the source of the env map construction.
- Add a pre-flight validation of the env map in caller code to fail fast with a clear message.
Example fix
// before
env := map[string]string{"MY-VAR": "x", "1BAD": "y"}
_, err := genconn.BuildShellCommand(genconn.CommandSpec{Cmd: "run", Env: env}) // errors
// after
env := map[string]string{"MY-VAR": "x", "1BAD": "y"}
safeEnv := make(map[string]string)
for k, v := range env {
if regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`).MatchString(k) {
safeEnv[k] = v
} else {
log.Printf("dropping invalid env key %q", k)
}
}
cmdStr, err := genconn.BuildShellCommand(genconn.CommandSpec{Cmd: "run", Env: safeEnv}) Defensive patterns
Strategy: validation
Validate before calling
var validEnvKey = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`)
func sanitizeEnv(env map[string]string) map[string]string {
safe := make(map[string]string, len(env))
for k, v := range env {
if validEnvKey.MatchString(k) {
safe[k] = v
}
}
return safe
}
// usage: BuildShellCommand(CommandSpec{Cmd: cmd, Env: sanitizeEnv(env)}) Type guard
func isValidEnvKey(k string) bool {
for i, c := range k {
if !(c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(i > 0 && c >= '0' && c <= '9')) {
return false
}
}
return len(k) > 0
} Try / catch
cmdStr, err := genconn.BuildShellCommand(spec)
if err != nil {
var badKey string
if fmt.Sscanf(err.Error(), "invalid environment variable name: %q", &badKey) == 1 {
return fmt.Errorf("fix env key %q in caller config (allowed: [A-Za-z_][A-Za-z0-9_]*)", badKey)
}
return err
} Prevention
- Never build Env from raw K=V strings — split them into map keys/values
- Pre-sanitize env maps with the same regex the library uses
- Watch for platform env keys that are illegal in sh (e.g. 'ProgramFiles(x86)')
- Validate env from user config at load time, not at command build time
- Keep keys ASCII; non-ASCII keys will be rejected by the regex
When it happens
Trigger: CommandSpec.Env contains a key that is empty, contains characters outside [a-zA-Z0-9_], starts with a digit, or contains '=' (already an assignment) — e.g. env maps built from raw strings like "FOO=bar" instead of key/value pairs.
Common situations: Passing OS environment strings (os.Environ() output split incorrectly), Windows-style or WSL env values with odd characters in the key, config-driven env vars with typos like 'MY-VAR' or '1PATH', or copying whole env blocks including keys like 'ProgramFiles(x86)'.
Related errors
- cwd is empty
- Image too large (>5MB)
- Unsupported or invalid image type: ${blob.type}
- Invalid CSS color: ${String(color)}
- Invalid CSS color: ${color}
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/363a154ed2bf942b.
Report an issue: GitHub.