Skip to content

bug: textarea infinite loop when pressing alt+left on empty input #1652

@Acksell

Description

@Acksell

Describe the bug

Pressing alt+b or alt+left (the WordBackward binding) on an empty textarea.Model causes an infinite loop that freezes the entire Bubble Tea event loop. The UI becomes completely unresponsive.

wordLeft() in textarea/textarea.go:923-929 has an unconditional for {} loop whose only exit is finding a non-space rune under the cursor. When the textarea is empty (m.value is [][]rune{ {} }, cursor at row=0, col=0), characterLeft(true) is a no-op and the break condition col < len(value[row]) evaluates to 0 < 0 → false, so the loop spins forever.

Note: doWordRight() already handles this correctly — it checks for end-of-text before looping.

Setup

  • charm.land/bubbles/v2 v2.0.0
  • OS: macOS
  • Shell: zsh
  • Terminal emulator: Ghostty
  • Terminal multiplexer: N/A

To Reproduce

  1. Create a textarea.Model with default (empty) content
  2. Focus the textarea
  3. Without typing anything, press alt+b or alt+left
  4. The UI freezes indefinitely

Source Code

package main

import (
	"fmt"
	"time"

	"charm.land/bubbles/v2/textarea"
	tea "charm.land/bubbletea/v2"
)

type model struct {
	ta textarea.Model
}

func (m model) Init() tea.Cmd {
	return m.ta.Focus()
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd
	m.ta, cmd = m.ta.Update(msg)
	return m, cmd
}

func (m model) View() tea.View {
	return tea.NewView(fmt.Sprintf(
		"Press alt+b or alt+left to freeze.\n\n%s",
		m.ta.View(),
	))
}

func main() {
	ta := textarea.New()
	ta.SetHeight(3)
	ta.SetWidth(40)
	ta.Placeholder = "type something, or don't..."

	p := tea.NewProgram(model{ta: ta})
	go func() {
		time.Sleep(10 * time.Second)
		fmt.Println("\n[timeout] program was stuck — killed")
		p.Kill()
	}()
	if _, err := p.Run(); err != nil {
		fmt.Println("error:", err)
	}
}

Expected behavior

Pressing alt+b / alt+left on an empty textarea should be a no-op (there is no word to the left of position 0,0), not freeze the program.

Screenshots

N/A

Additional context

Suggested fix — add a boundary check at the top of wordLeft(), mirroring what doWordRight() already does:

func (m *Model) wordLeft() {
	if m.row == 0 && m.col == 0 {
		return
	}
	// ... rest unchanged
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions