lima-vm/lima · error

failed to unmarshal %#q as %T: %w

Error message

failed to unmarshal %#q as %T: %w

What it means

The hostagent watches the instance's event log/stream and json.Unmarshals each line into an events.Event. When a line is not valid JSON (or not a JSON object matching the Event schema), Watch returns this wrapped error including the raw text and the underlying unmarshal error. It guards against corrupt or truncated event streams.

Source

Thrown at pkg/hostagent/events/watcher.go:66

loop:
	for {
		select {
		case <-ctx.Done():
			break loop
		case line := <-haStdoutTail.Lines:
			if line == nil {
				break loop
			}
			if line.Err != nil {
				logrus.Error(line.Err)
			}
			if line.Text == "" {
				continue
			}
			var ev Event
			if err := json.Unmarshal([]byte(line.Text), &ev); err != nil {
				return fmt.Errorf("failed to unmarshal %#q as %T: %w", line.Text, ev, err)
			}
			logrus.WithField("event", ev).Debugf("received an event")
			if !begin.IsZero() && ev.Time.Before(begin) {
				continue
			}
			if stop := onEvent(ev); stop {
				return nil
			}
		case line := <-haStderrTail.Lines:
			if line == nil {
				break loop
			}
			if line.Err != nil {
				logrus.Error(line.Err)
			}
			if propagateStderr {
				logrusutil.PropagateJSON(logrus.StandardLogger(), []byte(line.Text), "[hostagent] ", begin)
			}

View on GitHub (pinned to dd909d0973)

Solutions

  1. Delete the corrupt event log (e.g. ~/.lima/<instance>/ha.log or the event pipe file) and restart the instance so a fresh log is created
  2. Recreate the instance with 'limactl delete <name>' then 'limactl start <name>' if the log keeps regenerating corrupt lines
  3. Check disk space and filesystem health; truncated writes often come from ENOSPC
  4. Inspect the raw line shown in the error to identify what wrote the non-JSON content
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the event log before watching
lines, _ := os.ReadFile(eventLogPath)
for i, l := range bytes.Split(lines, []byte('\n')) {
	if len(l) == 0 { continue }
	var probe map[string]any
	if err := json.Unmarshal(l, &probe); err != nil {
		log.Printf("corrupt event line %d: %v", i+1, err)
	}
}

Type guard

func isValidEventLine(text string) bool {
	var ev events.Event
	return json.Unmarshal([]byte(text), &ev) == nil
}

Try / catch

if err := w.Watch(ctx, begin, onEvent); err != nil {
	var unw *json.UnmarshalTypeError
	if errors.As(err, &unw) || strings.Contains(err.Error(), "failed to unmarshal") {
		// rotate/truncate corrupt log and retry once
		os.Remove(eventLogPath)
		return w.Watch(ctx, begin, onEvent)
	}
	return err
}

Prevention

When it happens

Trigger: A line read from the event log file/socket is not valid JSON — e.g. a partially-written/truncated line after a crash, human-edited lima event log, or anything else writing non-JSON text into the same stream.

Common situations: VM killed mid-write leaving a half-flushed JSON line; disk full causing truncated writes; an older/newer guestagent writing a different event format; manually inspecting/appending to the lima event log with an editor.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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