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

  1. Set hook.Name to a bare file name with no path components (e.g. 'myhook' not 'subdir/myhook')
  2. Sanitize or reject user-supplied hook names before constructing the Hook
  3. 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

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


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/2c53a470098a7906. Report an issue: GitHub.