lima-vm/lima · error

failed to unmarshal xml

Error message

failed to unmarshal xml: %w

What it means

parseDiskutilImageAttachOutput parses the plist XML emitted by `diskutil image attach -plist -nomount`. This error wraps a failure from encoding/xml Unmarshal, meaning the output was not well-formed XML plist. diskutil's output is treated as a contract; malformed output breaks parsing.

Solutions

  1. Log the raw xmlStr alongside the wrapped error to inspect what diskutil returned
  2. Check the wrapped Unmarshal error for the malformed offset/element
  3. Verify the attach actually succeeded; handle non-plist error output before parsing
  4. Account for macOS-version differences in the plist schema used by the plist package

Example fix

// before
out, err := exec.Command("diskutil", "image", "attach", "--plist", "-nomount", path).Output()
// after
var stderr bytes.Buffer
cmd := exec.Command("diskutil", "image", "attach", "--plist", "-nomount", path)
cmd.Stderr = &stderr
out, err := cmd.Output()
if err != nil {
    return fmt.Errorf("attach failed: %w: %s", err, stderr.String()) // avoid parsing non-plist output
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: check output looks like a plist before parsing
func looksLikePlist(s string) bool { return strings.HasPrefix(strings.TrimSpace(s), "<?xml") }

Try / catch

disk, err := asifutil.DiskutilImageAttachNoMount(path)
if err != nil {
    if strings.Contains(err.Error(), "failed to unmarshal xml") {
        log.Printf("diskutil returned non-plist output; attach likely failed")
    }
    return err
}

Prevention

When it happens

Trigger: DiskutilImageAttachNoMount receives output that xml.Unmarshal rejects: truncated output, stderr mixed into stdout, a localized/error message instead of plist, or plist format variations encoding/xml cannot map into plist.Plist.

Common situations: Older/newer macOS emitting a different plist structure; attaching fails and diskutil prints a human-readable error instead of plist; output truncation from process signal or pipe buffering issues.

Related errors


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

Appendix: source

Thrown at pkg/imgutil/nativeimgutil/asifutil/asif_darwin.go:86

	resizeArgs := []string{"image", "resize", "--size", fmt.Sprintf("%d", size), path}
	if output, err := exec.CommandContext(context.Background(), "diskutil", resizeArgs...).CombinedOutput(); err != nil {
		return fmt.Errorf("failed to resize ASIF image %#q: %w: %s", path, err, output)
	}
	return nil
}

type AttachedDisk struct {
	Disk      string // whole disk device, e.g. "disk4" (GUID_partition_scheme)
	Container string // APFS container partition, e.g. "disk4s2" (Apple_APFS)
	Data      string // data volume device, e.g. "disk7s5"
}

// parseDiskutilImageAttachOutput parses the output of `diskutil image attach -plist -nomount <disk>`
// and returns the attached disk information.
func parseDiskutilImageAttachOutput(xmlStr string) (*AttachedDisk, error) {
	var p plist.Plist
	if err := xml.Unmarshal([]byte(xmlStr), &p); err != nil {
		return nil, fmt.Errorf("failed to unmarshal xml: %w", err)
	}

	if p.Value.Dict == nil {
		return nil, errors.New("unexpected plist format: missing root dict")
	}

	seVal, ok := p.Value.Dict["system-entities"]
	if !ok || len(seVal.Array) == 0 {
		return nil, errors.New("unexpected plist format: missing system-entities array")
	}

	result := &AttachedDisk{}
	for _, devEnt := range seVal.Array {
		devDict := devEnt.Dict
		if devDict == nil {
			continue
		}
		devEntry, hasDevEntry := devDict["dev-entry"]

View on GitHub (pinned to dd909d0973)