vitessio/vitess · error
invalid hook name %q: %v
Error message
invalid hook name %q: %v
What it means
After resolving the vthook directory, the hook name is validated with fileutil.SafePathJoin to prevent path traversal or illegal names. If the name is unsafe (contains '/', '..', absolute path components, or other rejected characters), findHook returns HOOK_INVALID_NAME. This is a security guard: hook names must be simple names of scripts inside the vthook directory.
Source
Thrown at go/vt/hook/hook.go:111
}
// NewHookWithEnv returns a Hook object with the provided name, params and ExtraEnv.
func NewHookWithEnv(name string, params []string, env map[string]string) *Hook {
return &Hook{Name: name, Parameters: params, ExtraEnv: env}
}
// findHook tries to locate the hook, and returns the exec.Cmd for it.
func (hook *Hook) findHook(ctx context.Context) (*exec.Cmd, int, error) {
// Find our root.
root, err := vtenv.VtRoot()
if err != nil {
return nil, HOOK_VTROOT_ERROR, fmt.Errorf("cannot get VTROOT: %v", err)
}
// See if the hook exists.
vthook, err := fileutil.SafePathJoin(filepath.Join(root, "vthook"), hook.Name)
if err != nil {
return nil, HOOK_INVALID_NAME, fmt.Errorf("invalid hook name %q: %v", hook.Name, err)
}
_, err = os.Stat(vthook)
if err != nil {
if os.IsNotExist(err) {
return nil, HOOK_DOES_NOT_EXIST, fmt.Errorf("missing hook %v", vthook)
}
return nil, HOOK_STAT_FAILED, fmt.Errorf("cannot stat hook %v: %v", vthook, err)
}
// Configure the command.
log.Info(fmt.Sprintf("hook: executing hook: %v %v", vthook, strings.Join(hook.Parameters, " ")))
cmd := exec.CommandContext(ctx, vthook, hook.Parameters...)
if len(hook.ExtraEnv) > 0 {
cmd.Env = os.Environ()
for key, value := range hook.ExtraEnv {
cmd.Env = append(cmd.Env, key+"="+value)
}View on GitHub (pinned to 01a25a7d17)
Solutions
- Set hook.Name to a bare file name with no path components (e.g. 'myhook' not 'subdir/myhook')
- Sanitize or reject user-supplied hook names before constructing the Hook
- Check the %q in the message to see the exact rejected name and which character caused SafePathJoin to fail
Example fix
// before
hook := &hook.Hook{Name: path.Join("bin", "cleanup.sh")}
// after
hook := &hook.Hook{Name: "cleanup.sh"} Defensive patterns
Strategy: validation
Validate before calling
func validHookName(name string) bool {
if name == "" || strings.ContainsAny(name, "/\\") || strings.Contains(name, "..") {
return false
}
return name == filepath.Base(name)
} Try / catch
hr := h.ExecuteContext(ctx)
if hr.ExitStatus == hook.HOOK_INVALID_NAME {
return fmt.Errorf("rejecting hook %q: invalid name", h.Name)
} Prevention
- Restrict hook names to [A-Za-z0-9._-] with no path separators
- Never build hook names from unsanitized user input
- Use fileutil.SafePathJoin yourself when constructing paths to hook scripts
When it happens
Trigger: Calling any Hook execution API with hook.Name containing path separators, '..' segments, a leading '/', empty/illegal characters, or otherwise failing fileutil.SafePathJoin validation.
Common situations: Building hook names dynamically from user input or config values that include subdirectories (e.g. 'tools/myhook.sh'); names copied from file paths rather than bare script names.
Related errors
- ErrPartSize
- BeforeSchema differs
- AfterSchema differs
- UUID values must be unique
- BaseKeyspace is required for SNAPSHOT keyspaces
AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01).
Data as JSON: /api/errors/2c53a470098a7906.
Report an issue: GitHub.