go-kratos/kratos · error

invalid formatting for map key

Error message

invalid formatting for map key

What it means

errInvalidFormatMapKey (encoding/form/proto_decode.go:25) is returned by parseURLQueryMapKey via populateMapField when the URL query key addressing a proto map field cannot be split into (field, key). Accepted forms are 'field[key]=value' (brackets wrapping the whole tail: startIndex>0, endIndex after it, nothing beyond ']') or exactly one separator as in 'field.key=value'. Everything else - no bracket and dot-count != 1, empty field part, bracket at position 0, '[' after ']', trailing characters after ']', unmatched/multiple brackets - fails.

Source

Thrown at encoding/form/proto_decode.go:25

	"net/url"
	"strconv"
	"strings"
	"time"

	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
	"google.golang.org/protobuf/types/known/durationpb"
	"google.golang.org/protobuf/types/known/fieldmaskpb"
	"google.golang.org/protobuf/types/known/structpb"
	"google.golang.org/protobuf/types/known/timestamppb"
	"google.golang.org/protobuf/types/known/wrapperspb"
)

const fieldSeparator = "."

var errInvalidFormatMapKey = errors.New("invalid formatting for map key")

// DecodeValues decode url value into proto message.
func DecodeValues(msg proto.Message, values url.Values) error {
	for key, values := range values {
		if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), values); err != nil {
			return err
		}
	}
	return nil
}

func populateFieldValues(v protoreflect.Message, fieldPath []string, values []string) error {
	if len(fieldPath) < 1 {
		return errors.New("no field path")
	}
	if len(values) < 1 {
		return errors.New("no value provided")
	}

View on GitHub (pinned to 668db92c2c)

Solutions

  1. Use the canonical bracket form for map fields: 'mymap[key]=value'
  2. Or the single-dot form with exactly one dot: 'mymap.key=value'
  3. Avoid mixing nested message dots and bracket keys in one key; for a map inside a message use 'msg.field[key]=value' so fieldPath length 2 hits the map branch
  4. Sanitize/validate incoming query keys before DecodeValues when keys come from untrusted clients

Example fix

// before
values := url.Values{}
values.Set("labels.meta.app", "kratos") // two dots, no brackets -> invalid
form.DecodeValues(msg, values)

// after
values := url.Values{}
values.Set("labels[meta.app]", "kratos") // field[key]
// or exactly one dot:
values.Set("labels.meta", "kratos")
Defensive patterns

Strategy: validation

Validate before calling

var mapKeyRe = regexp.MustCompile(`^[^\[\].]+(\[[^\[\]]*\])?$`)

func validQueryKeys(q url.Values) error {
    for k := range q {
        if !mapKeyRe.MatchString(k) && strings.Count(k, ".") > 1 {
            return fmt.Errorf("suspicious map key %q", k)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: Form/query binding a proto message with a map field where the query contains keys like 'map[]', 'map[', 'kratos]', '[[]', 'map.kratos.v2' (two dots, no brackets), '.kratos' (empty field), or 'map[kratos]=' style malformed tails. The map branch is entered when the field descriptor is a map and fieldPath length is 2 (line 51) or on the post-subfield path (line 58), then strings.Join(fieldPath, ".") is re-parsed by parseURLQueryMapKey.

Common situations: Hand-built query strings from templates or clients that URL-encode brackets wrongly; using both dot nesting and bracket keys in the same key ('a.b[c]'); nested message paths whose second element is the map key written with extra dots; migrating from go-playground/form style keys with subtle syntax drift (the code comments reference that format).

Related errors


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/c248cc1f017fa072. Report an issue: GitHub.