[GH-ISSUE #1098] Infinite Recursion In Focus #796

Closed
opened 2026-03-04 01:07:49 +03:00 by kerem · 3 comments
Owner

Originally created by @bn-bmagee on GitHub (Jun 4, 2025).
Original GitHub issue: https://github.com/rivo/tview/issues/1098

I am encountering an infinite recursion when trying to focus within a Form:

 0  0x00000000004728c4 in runtime.throw
    at ./.local/share/go/src/runtime/panic.go:1064
 1  0x0000000000456a9b in runtime.newstack
    at ./.local/share/go/src/runtime/stack.go:1117
 2  0x000000000046e1cc in runtime.mallocgc
    at ./.local/share/go/src/runtime/malloc.go:983
 3  0x000000000040eb85 in runtime.newobject
    at ./.local/share/go/src/runtime/malloc.go:1386
 4  0x000000000059d4dd in github.com/rivo/tview.(*Form).Focus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:715
 5  0x000000000059dde8 in github.com/rivo/tview.(*Form).Focus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:729
 6  0x000000000059de42 in github.com/rivo/tview.(*Form).Focus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:740
 7  0x00000000005a05f2 in github.com/rivo/tview.(*InputField).Focus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/inputfield.go:456
 8  0x00000000005971cd in github.com/rivo/tview.(*Application).SetFocus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:824
 9  0x0000000000597250 in github.com/rivo/tview.(*Application).SetFocus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:825
10  0x000000000059dabc in github.com/rivo/tview.(*Form).Focus.func3.deferwrap1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:772
11  0x00000000004398de in runtime.deferreturn
    at ./.local/share/go/src/runtime/panic.go:611
12  0x000000000059da2b in github.com/rivo/tview.(*Form).Focus.func3
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773
13  0x000000000059d84a in github.com/rivo/tview.(*Form).Focus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773
14  0x000000000059dde8 in github.com/rivo/tview.(*Form).Focus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:729
15  0x000000000059de42 in github.com/rivo/tview.(*Form).Focus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:740
16  0x00000000005a05f2 in github.com/rivo/tview.(*InputField).Focus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/inputfield.go:456
17  0x00000000005971cd in github.com/rivo/tview.(*Application).SetFocus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:824
18  0x0000000000597250 in github.com/rivo/tview.(*Application).SetFocus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:825
19  0x000000000059dabc in github.com/rivo/tview.(*Form).Focus.func3.deferwrap1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:772
20  0x00000000004398de in runtime.deferreturn
    at ./.local/share/go/src/runtime/panic.go:611
21  0x000000000059da2b in github.com/rivo/tview.(*Form).Focus.func3
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773
22  0x000000000059d84a in github.com/rivo/tview.(*Form).Focus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773
23  0x000000000059dde8 in github.com/rivo/tview.(*Form).Focus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:729
24  0x000000000059de42 in github.com/rivo/tview.(*Form).Focus.func1
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:740
25  0x00000000005a05f2 in github.com/rivo/tview.(*InputField).Focus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/inputfield.go:456
26  0x00000000005971cd in github.com/rivo/tview.(*Application).SetFocus
    at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:824
27  0x0000000000597250 in github.com/rivo/tview.(*Application).SetFocus.func1

(this continues)

I made a simple case that reproduces the issue at least on my computer:

package main

import (
	"fmt"

	"github.com/rivo/tview"
)

func main() {
	app := tview.NewApplication()

	form := tview.NewForm()
	fieldOne := tview.NewInputField().SetLabel("One")
	_ = form.AddFormItem(fieldOne)

	var buttonOne, buttonTwo, buttonThree *tview.Button
	_ = form.AddButton("Run", func() {
		_ = fieldOne.SetDisabled(true)
		_ = buttonOne.SetDisabled(true)
		_ = buttonTwo.SetDisabled(true)
		_ = buttonThree.SetDisabled(false)

		_ = app.SetFocus(buttonThree)

		go func() {
			app.QueueUpdateDraw(func() {
				_ = fieldOne.SetDisabled(false)
				_ = buttonOne.SetDisabled(false)
				_ = buttonTwo.SetDisabled(false)
				_ = buttonThree.SetDisabled(true)

				_ = app.SetFocus(buttonTwo)
			})
		}()
	})
	buttonOne = form.GetButton(form.GetButtonIndex("Run"))
	_ = form.AddButton("Back", nil)
	buttonTwo = form.GetButton(form.GetButtonIndex("Back"))
	_ = form.AddButton("Kill", nil)
	buttonThree = form.GetButton(form.GetButtonIndex("Kill"))

	app.SetRoot(form, true)

	err := app.Run()
	if err != nil {
		fmt.Println(err.Error())
	}
}

Pressing "Run" one time will work, but when it is pressed a second time it creates the recursive loop. I'm not sure if I'm doing anything "illegal" with this program, so if I am please let me know. If not, I suspect something is wrong here:

// SetFocus sets the focus to a new primitive. All key events will be directed
// down the hierarchy (starting at the root) until a primitive handles them,
// which per default goes towards the focused primitive.
//
// Blur() will be called on the previously focused primitive. Focus() will be
// called on the new primitive.
func (a *Application) SetFocus(p Primitive) *Application {
	a.Lock()
	if a.focus != nil {
		a.focus.Blur()
	}
	a.focus = p
	if a.screen != nil {
		a.screen.HideCursor()
	}
	a.Unlock()
	if p != nil {
		p.Focus(func(p Primitive) {
			a.SetFocus(p)
		})
	}

	return a
}

This looks to me like an infinite recursion waiting to happen that is avoided in many cases by the delegation function never being invoked.

Originally created by @bn-bmagee on GitHub (Jun 4, 2025). Original GitHub issue: https://github.com/rivo/tview/issues/1098 I am encountering an infinite recursion when trying to focus within a Form: ``` 0 0x00000000004728c4 in runtime.throw at ./.local/share/go/src/runtime/panic.go:1064 1 0x0000000000456a9b in runtime.newstack at ./.local/share/go/src/runtime/stack.go:1117 2 0x000000000046e1cc in runtime.mallocgc at ./.local/share/go/src/runtime/malloc.go:983 3 0x000000000040eb85 in runtime.newobject at ./.local/share/go/src/runtime/malloc.go:1386 4 0x000000000059d4dd in github.com/rivo/tview.(*Form).Focus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:715 5 0x000000000059dde8 in github.com/rivo/tview.(*Form).Focus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:729 6 0x000000000059de42 in github.com/rivo/tview.(*Form).Focus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:740 7 0x00000000005a05f2 in github.com/rivo/tview.(*InputField).Focus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/inputfield.go:456 8 0x00000000005971cd in github.com/rivo/tview.(*Application).SetFocus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:824 9 0x0000000000597250 in github.com/rivo/tview.(*Application).SetFocus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:825 10 0x000000000059dabc in github.com/rivo/tview.(*Form).Focus.func3.deferwrap1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:772 11 0x00000000004398de in runtime.deferreturn at ./.local/share/go/src/runtime/panic.go:611 12 0x000000000059da2b in github.com/rivo/tview.(*Form).Focus.func3 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773 13 0x000000000059d84a in github.com/rivo/tview.(*Form).Focus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773 14 0x000000000059dde8 in github.com/rivo/tview.(*Form).Focus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:729 15 0x000000000059de42 in github.com/rivo/tview.(*Form).Focus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:740 16 0x00000000005a05f2 in github.com/rivo/tview.(*InputField).Focus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/inputfield.go:456 17 0x00000000005971cd in github.com/rivo/tview.(*Application).SetFocus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:824 18 0x0000000000597250 in github.com/rivo/tview.(*Application).SetFocus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:825 19 0x000000000059dabc in github.com/rivo/tview.(*Form).Focus.func3.deferwrap1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:772 20 0x00000000004398de in runtime.deferreturn at ./.local/share/go/src/runtime/panic.go:611 21 0x000000000059da2b in github.com/rivo/tview.(*Form).Focus.func3 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773 22 0x000000000059d84a in github.com/rivo/tview.(*Form).Focus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:773 23 0x000000000059dde8 in github.com/rivo/tview.(*Form).Focus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:729 24 0x000000000059de42 in github.com/rivo/tview.(*Form).Focus.func1 at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/form.go:740 25 0x00000000005a05f2 in github.com/rivo/tview.(*InputField).Focus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/inputfield.go:456 26 0x00000000005971cd in github.com/rivo/tview.(*Application).SetFocus at ./go/pkg/mod/github.com/rivo/tview@v0.0.0-20250501113434-0c592cd31026/application.go:824 27 0x0000000000597250 in github.com/rivo/tview.(*Application).SetFocus.func1 ``` (this continues) I made a simple case that reproduces the issue at least on my computer: ``` package main import ( "fmt" "github.com/rivo/tview" ) func main() { app := tview.NewApplication() form := tview.NewForm() fieldOne := tview.NewInputField().SetLabel("One") _ = form.AddFormItem(fieldOne) var buttonOne, buttonTwo, buttonThree *tview.Button _ = form.AddButton("Run", func() { _ = fieldOne.SetDisabled(true) _ = buttonOne.SetDisabled(true) _ = buttonTwo.SetDisabled(true) _ = buttonThree.SetDisabled(false) _ = app.SetFocus(buttonThree) go func() { app.QueueUpdateDraw(func() { _ = fieldOne.SetDisabled(false) _ = buttonOne.SetDisabled(false) _ = buttonTwo.SetDisabled(false) _ = buttonThree.SetDisabled(true) _ = app.SetFocus(buttonTwo) }) }() }) buttonOne = form.GetButton(form.GetButtonIndex("Run")) _ = form.AddButton("Back", nil) buttonTwo = form.GetButton(form.GetButtonIndex("Back")) _ = form.AddButton("Kill", nil) buttonThree = form.GetButton(form.GetButtonIndex("Kill")) app.SetRoot(form, true) err := app.Run() if err != nil { fmt.Println(err.Error()) } } ``` Pressing "Run" one time will work, but when it is pressed a second time it creates the recursive loop. I'm not sure if I'm doing anything "illegal" with this program, so if I am please let me know. If not, I suspect something is wrong here: ``` // SetFocus sets the focus to a new primitive. All key events will be directed // down the hierarchy (starting at the root) until a primitive handles them, // which per default goes towards the focused primitive. // // Blur() will be called on the previously focused primitive. Focus() will be // called on the new primitive. func (a *Application) SetFocus(p Primitive) *Application { a.Lock() if a.focus != nil { a.focus.Blur() } a.focus = p if a.screen != nil { a.screen.HideCursor() } a.Unlock() if p != nil { p.Focus(func(p Primitive) { a.SetFocus(p) }) } return a } ``` This looks to me like an infinite recursion waiting to happen that is avoided in many cases by the delegation function never being invoked.
kerem closed this issue 2026-03-04 01:07:49 +03:00
Author
Owner

@rivo commented on GitHub (Aug 27, 2025):

Unfortunately, I cannot reproduce the issue with your code. It behaves as expected, no infinite recursion.

Note that I'm about to push some major changes to tview but I've tried your code with and without these changes. No issues.

<!-- gh-comment-id:3228150527 --> @rivo commented on GitHub (Aug 27, 2025): Unfortunately, I cannot reproduce the issue with your code. It behaves as expected, no infinite recursion. Note that I'm about to push some major changes to `tview` but I've tried your code with and without these changes. No issues.
Author
Owner

@bn-bmagee commented on GitHub (Aug 27, 2025):

Well, I suppose there's not much to be done if you can't reproduce the issue.

<!-- gh-comment-id:3228641071 --> @bn-bmagee commented on GitHub (Aug 27, 2025): Well, I suppose there's not much to be done if you can't reproduce the issue.
Author
Owner

@rivo commented on GitHub (Aug 27, 2025):

Feel free to open a new issue if you find a way to reliably reproduce this.

<!-- gh-comment-id:3229481734 --> @rivo commented on GitHub (Aug 27, 2025): Feel free to open a new issue if you find a way to reliably reproduce this.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/tview#796
No description provided.