lima-vm/lima · error

expected <key> element, got <%s>

Error message

expected <key> element, got <%s>

What it means

In a plist <dict>, keys must alternate with values and keys are encoded as <key> elements. Dict.UnmarshalXML (pkg/plist/plist.go:194) requires every start element it reads inside a <dict> to be <key>; anything else (a value element at key position, nesting mistakes, missing value after a key) triggers "expected <key> element, got <%s>".

Source

Thrown at pkg/plist/plist.go:194

		}
	}
}

func (d *Dict) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {
	*d = make(map[string]Value)

	for {
		tok, err := dec.Token()
		if err != nil {
			if errors.Is(err, io.EOF) {
				return nil
			}
			return err
		}
		switch t := tok.(type) {
		case xml.StartElement:
			if t.Name.Local != "key" {
				return fmt.Errorf("expected <key> element, got <%s>", t.Name.Local)
			}
			var key string
			if err := dec.DecodeElement(&key, &t); err != nil {
				return err
			}
			var vs xml.StartElement
			for {
				vt, err := dec.Token()
				if err != nil {
					return err
				}
				if se, ok := vt.(xml.StartElement); ok {
					vs = se
					break
				}
			}
			var v Value
			if err := dec.DecodeElement(&v, &vs); err != nil {

View on GitHub (pinned to dd909d0973)

Solutions

  1. Ensure every <dict> child sequence is strictly alternating <key>k</key><value/>, <key>k</key><value/> …
  2. Look at the %s tag in the error to find the misplaced element and add or reorder the missing <key>
  3. Validate the plist with plutil -lint (macOS) or xmllint against the plist DTD before parsing
  4. Regenerate the dict programmatically instead of hand-editing to guarantee key/value pairing

Example fix

// before (fails: value where key expected)
<dict>
  <string>value</string>
  <key>k</key><string>v</string>
</dict>
// after
<dict>
  <key>name</key><string>value</string>
  <key>k</key><string>v</string>
</dict>
Defensive patterns

Strategy: validation

Validate before calling

func validateDictAlternation(data []byte) error {
	dec := xml.NewDecoder(bytes.NewReader(data))
	depth, expectKey := 0, false
	for {
		tok, err := dec.Token()
		if err == io.EOF { return nil }
		if err != nil { return err }
		se, ok := tok.(xml.StartElement)
		if !ok { continue }
		switch se.Name.Local {
		case "dict":
			depth++; expectKey = true
		case "key":
			if depth > 0 && !expectKey { return errors.New("<key> without preceding pair") }
			expectKey = false
		default:
			if depth > 0 && expectKey { return fmt.Errorf("expected <key>, got <%s>", se.Name.Local) }
			expectKey = true
		}
	}
}

Try / catch

var v plist.Value
if err := xml.Unmarshal(data, &v); err != nil {
	if strings.HasPrefix(err.Error(), "expected <key> element, got <") {
		tag := strings.TrimSuffix(strings.TrimPrefix(err.Error(), "expected <key> element, got <"), ">")
		// locate and fix the misplaced element in the <dict>
	}
	return err
}

Prevention

When it happens

Trigger: Decoding a plist whose <dict> contains a value element (e.g. <string>) where a <key> is expected — typically because a key/value pair is incomplete, two values were adjacent, or elements were mis-ordered after hand editing.

Common situations: Hand-edited plists that dropped a value after a <key>, duplicated keys' values, or placed children out of order; programmatic generators that emit value elements before keys; truncated documents.

Related errors


AI-assisted analysis of lima-vm/lima@dd909d0973 (2026-09-01). Data as JSON: /api/errors/4f7892159939356f. Report an issue: GitHub.