go-playground/validator · error

Tag '%s' either contains restricted characters or is the sam

Error message

Tag '%s' either contains restricted characters or is the same as a restricted tag needed for normal operation

What it means

validator_instance.go:754 panics in registerValidation when a non-baked-in custom tag name is already in restrictedTags or contains restrictedTagChars ('-', '=', etc.). Just like RegisterAlias, custom validator names must not collide with built-in tags or contain tag-grammar characters, because that would corrupt tag parsing. Note the same function returns an error (not a panic) when fn is nil — the panic is specific to the tag name.

Source

Thrown at validator_instance.go:754

		err = vd.errs
		vd.errs = nil
	}
	v.pool.Put(vd)
	return
}

func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
	if len(tag) == 0 {
		return errors.New("function Key cannot be empty")
	}

	if fn == nil {
		return errors.New("function cannot be empty")
	}

	_, ok := restrictedTags[tag]
	if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
		panic(fmt.Sprintf(restrictedTagErr, tag))
	}
	v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidationOnNil: nilCheckable}
	return nil
}

View on GitHub (pinned to facf128d2e)

Solutions

  1. Choose a custom tag name that is not in the restrictedTags denylist and contains only safe characters (letters, digits, underscore).
  2. If you need different behavior than a baked-in tag, register it under a new name and use that name in your struct tags.
  3. Sanitize/validate dynamic tag names before calling RegisterValidation (reject names containing '-', '=', '.', ',', '|').
  4. For nil-function mistakes, check fn != nil before registering — that path returns an error rather than panicking but is easy to conflate.
  5. Register all custom validators in package init at startup so panics are caught at boot.

Example fix

// before
v.RegisterValidation("my-validator", myFunc)  // '-' restricted
v.RegisterValidation("email", overrideEmail) // restricted tag

// after
v.RegisterValidation("myValidator", myFunc)
v.RegisterValidation("customEmail", overrideEmail)
Defensive patterns

Strategy: validation

Validate before calling

func safeRegisterValidation(v *validator.Validate, tag string, fn validator.FuncCtx) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("invalid tag %q: %v", tag, r)
        }
    }()
    return v.RegisterValidationCtx(tag, fn)
}

Type guard

var validTagRe = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)

func isSafeCustomTag(tag string) bool {
    return validTagRe.MatchString(tag)
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("RegisterValidation rejected %q: %v", tag, r)
    }
}()
err = v.RegisterValidation(tag, fn)

Prevention

When it happens

Trigger: v.RegisterValidation("required", myFunc) — re-registering a restricted built-in name; v.RegisterValidation("my-func", fn) with a dash or other restrictedTagChars in the name; wrapping RegisterValidation with dynamically built tag names that include restricted characters; attempting to override email/uuid with custom logic.

Common situations: Teams trying to override baked-in validators (upgrade path from older validator versions that allowed it); naming custom validators with kebab-case; user-supplied or config-driven tag names passed straight to RegisterValidation; copy-pasted registration code where someone renamed the tag adding separators.

Related errors


AI-assisted analysis of go-playground/validator@facf128d2e (2026-09-02). Data as JSON: /api/errors/53b37ada418f8999. Report an issue: GitHub.