go-playground/validator · critical

'endkeys' tag encountered without a corresponding 'keys' tag

Error message

'endkeys' tag encountered without a corresponding 'keys' tag

What it means

An `endkeys` tag was parsed without a matching `keys` tag in the same tag chain. cache.go panics with keysTagNotDefined ("'endkeys' tag encountered without a corresponding 'keys' tag") when `endkeys` appears where no keys block is open (specifically when it is not the terminating tag after a keys block).

Source

Thrown at cache.go:242

			for ; i < len(tags); i++ {
				b = append(b, tags[i]...)
				b = append(b, ',')

				if tags[i] == endKeysTag {
					break
				}
			}

			current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false)

		case endKeysTag:
			current.typeof = typeEndKeys

			// if there are more in tags then there was no keysTag defined
			// and an error should be thrown
			if i != len(tags)-1 {
				panic(keysTagNotDefined)
			}
			return

		case omitzero:
			current.typeof = typeOmitZero
			continue

		case omitempty:
			current.typeof = typeOmitEmpty

		case omitnil:
			current.typeof = typeOmitNil

		case structOnlyTag:
			current.typeof = typeStructOnly

		case noStructLevelTag:
			current.typeof = typeNoStructLevel

View on GitHub (pinned to facf128d2e)

Solutions

  1. Ensure every `endkeys` is preceded by `keys` inside a `dive` block: `dive,keys,<...>,endkeys,<...>`
  2. Remove the orphaned `endkeys` tag
  3. Fix generated/aliased tag strings to emit the full keys/endkeys pair
  4. Validate a sample struct in unit tests to catch syntax panics at build time

Example fix

// before
M map[string]string `validate:"dive,endkeys,min=1"`
// after
M map[string]string `validate:"dive,keys,min=1,endkeys,min=1"`
Defensive patterns

Strategy: validation

Validate before calling

func checkEndKeys(t reflect.StructTag) error {
    tags := strings.Split(t.Get("validate"), ",")
    depth := 0
    for _, tg := range tags {
        switch tg {
        case "keys": depth++
        case "endkeys":
            if depth == 0 { return errors.New("endkeys without keys") }
            depth--
        }
    }
    return nil
}

Try / catch

defer func() {
    if r := recover(); r != nil {
        err = fmt.Errorf("invalid tag structure: %v", r)
    }
}()
_ = validate.Struct(sample)

Prevention

When it happens

Trigger: Tag like `validate:"dive,endkeys,required"` or `validate:"endkeys"` — endkeys without keys; also endkeys followed by more tags but no preceding keys.

Common situations: Deleting a `keys,...` segment during editing while leaving `endkeys`; misunderstanding of the dive,keys,endkeys pairing for map validation.

Related errors


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