jesseduffield/lazygit · error

All gui.spinner.frames entries must have the same width.

Error message

All gui.spinner.frames entries must have the same width.

What it means

Validation error from validateSpinner (user_config_validation.go:108). All gui.spinner.frames entries must have the same display width (measured with utils.StringWidth, unicode-aware); mixed-width frames make the spinner jitter and break layout, so config load fails. The first frame's width is the reference.

Source

Thrown at pkg/config/user_config_validation.go:108

	// conflict or popping a stash), so they must always be present; otherwise
	// that code would focus a hidden panel.
	for _, required := range []string{"files", "branches", "commits"} {
		if !seen[required] {
			return fmt.Errorf("gui.sidePanels: '%s' must be included; it can't be hidden.", required)
		}
	}
	return nil
}

func validateSpinner(spinner SpinnerConfig) error {
	if len(spinner.Frames) == 0 {
		return errors.New("gui.spinner.frames must not be empty.")
	}
	firstWidth := utils.StringWidth(spinner.Frames[0])
	if lo.SomeBy(spinner.Frames, func(frame string) bool {
		return utils.StringWidth(frame) != firstWidth
	}) {
		return errors.New("All gui.spinner.frames entries must have the same width.")
	}
	return nil
}

func validateDiffRenderers(diffRenderers []DiffRendererConfig) error {
	for _, diffRenderer := range diffRenderers {
		switch diffRenderer.Type {
		case "stdinFilter", "":
			if diffRenderer.Command == "" {
				return errors.New("git.diffRenderers: 'command' must be specified for diff renderer type 'stdinFilter'.")
			}
			if len(diffRenderer.Args) > 0 {
				return errors.New("git.diffRenderers: 'args' cannot be used with diff renderer type 'stdinFilter'.")
			}
		case "extDiff":
			if len(diffRenderer.Args) > 0 {
				return errors.New("git.diffRenderers: 'args' cannot be used with diff renderer type 'extDiff'.")
			}

View on GitHub (pinned to c477a2959b)

Solutions

  1. Inspect each frame for stray spaces or hidden characters (e.g. with a hex view)
  2. Use frames from a single character set — all braille, all ASCII, or all emoji
  3. Pad narrower frames with spaces to match the widest frame's column width
  4. Validate widths yourself with a unicode-width aware function before saving

Example fix

# before
gui:
  spinner:
    frames: ['⠋', '🌧️']  # width 1 vs width 2

# after
gui:
  spinner:
    frames: ['⠋', '⠙', '⠹', '⠸']  # uniform width 1
Defensive patterns

Strategy: type-guard

Validate before calling

import "github.com/mattn/go-runewidth"

func uniformSpinnerWidth(frames []string) bool {
    if len(frames) == 0 {
        return false
    }
    w := runewidth.StringWidth(frames[0])
    for _, f := range frames[1:] {
        if runewidth.StringWidth(f) != w {
            return false
        }
    }
    return true
}

Type guard

func isUniformWidthFrameSet(frames []string) bool {
    return uniformSpinnerWidth(frames)
}

Prevention

When it happens

Trigger: Mixing frames of differing widths, including double-width CJK/emoji frames with narrow ASCII ones, or combining '⠋' with '-'. utils.StringWidth accounts for East Asian wide characters, so width is visual columns, not rune count.

Common situations: Copy-pasting a spinner from a font/terminal that renders differently; mixing emoji (often width 2) with braille frames (width 1); hand-crafted frames with a trailing space in only some entries.

Related errors


AI-assisted analysis of jesseduffield/lazygit@c477a2959b (2026-08-15). Data as JSON: /api/errors/507deae9cf2b3f86. Report an issue: GitHub.