- 
                Notifications
    
You must be signed in to change notification settings  - Fork 1k
 
Description
Summary
A sub-model, when returning a custom command to signal an exit/state switch in the parent model, occasionally fails to close or transfer control reliably. The issue appears to be related to the Bubble Tea command/message lifecycle within nested model architecture, specifically in how the parent model processes the custom exit message (BackToMenuMsg) relative to model delegation.
Environment
- 
Bubble Tea Version: [ v1.3.4]
 - 
Go Version: [1.24.4]
 - 
OS, Windows 10/amd64
 - 
Terminal Emulator Visual Studio/DOS/Powershell
 
Bug Reproduce
Steps to reproduce the behaviour:
- Start the parent program, which initializes the child model (e.g., Kycamlmodel).
 - Interact with the child model (e.g., type a few characters).
 - Press the Escape key (esc) on the keyboard.
 - The child model returns a command: return m, sendBackToMenuCmd("view_account").
 
Expected Behaviour
- The parent model should:
 - Receive the BackToMenuMsg message.
 - Change its internal state (e.g., m.state = StateMenuBrowsing).
 - Update its active content type (m.activeContentType = msg.TargetView).
 - Render the new view (main menu or "view_account").
 
Actual Behaviour
The program remains stuck, displaying the child model's view (Kycamlmodel.View()), indicating the parent's state switch logic was not executed or was immediately overridden by delegation
Source Code - Sub Model
Below is an example source code snippet, that is common for all 35 sub models, running under the parent program - if needed to reproduce the behaviour.
// --- Kycamlmodel's Update Method ---
func (m Kycamlmodel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    // ... other code ...
    
    switch msg := msg.(type) {
    case tea.KeyMsg:
        if msg.String() == "esc" {
            // Child returns its model and the command to exit/switch state
            return m, sendBackToMenuCmd("view_account") 
        }
    }
    return m, nil 
}Source Code - Parent Model
There are no bugs in the Parent Model. Below are code snippets which are true for all Sub Models:
// --- 1. Custom Message Type (Data Carrier) ---
type BackToMenuMsg struct {}
 / --- 2. Standalone Command Factory (Globally Callable) ---
// This closure captures the 'target' string and creates the command.
func sendBackToMenuCmd(target string) tea.Cmd {
    return func() tea.Msg {
        return BackToMenuMsg{}
    }
}Update Method
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
	var cmd tea.Cmd    // Command returned by the current state's update logic or a child model
	var cmds []tea.Cmd // Slice to accumulate multiple commands
	// --- Global Key Handling (Quit First) ---
	// Check for global quit commands regardless of the current state.
	if key, ok := msg.(tea.KeyMsg); ok {
		if key.String() == "ctrl+c" || key.String() == "q" {
			m.quitting = true  // Set quitting flag for final View render
			return m, tea.Quit // Return the Quit command to terminate the program
		}
	}************* IMPORTANT CODE *************
switch msg := msg.(type) {
    case BackToMenuMsg: 
        // The child told us to switch state. We execute it immediately.
        m.state = StateMenuBrowsing 
        m.activeContentType = msg.TargetView 
        
        // Immediately return nil command; DO NOT delegate or continue.
        return m, nil 
    }************* SUB MODEL HANDLING METHODS, IN UPDATE *************
// ⬇️ STEP 2: Only delegate messages that are NOT the exit command ⬇️
    var subCmd tea.Cmd
    var updatedModel tea.Model
    
    if m.state == StateContentDisplay && m.activeContentType == "create_account" {
        
        updatedModel, subCmd = m.kycModel.Update(msg)
        
        // Safely assert the model back to the concrete type
        if kycModel, ok := updatedModel.(Kycamlmodel); ok {
            m.kycModel = kycModel
        }
        
        return m, subCmd
    }
    return m, nil
}************* ADDITIONAL CODE UNDER STATEMENUBROWSING; IN THE UPDATE METHOD *************
The following code snippet is the same for all 35 other Sub Models:
case StateMenuBrowsing:
	case "create_account":
		m.activeContentType = "create_account"
		m.createaccountModel = kmlNewModel()
		cmds = append(cmds, m.createaccountModel.Init())Additional context
So far, only three programs of the 35 fail to close when the escape key has been pressed. Everything I know has been tried to change this. For example, when watching the code execution in the Parent Model, for this method below:
func sendBackToMenuCmd(target string) tea.Cmd {
    return func() tea.Msg {
        return BackToMenuMsg{}
    }
}Conclusion
When the execution point hits the - "return func() tea.Msg", as seen in the code snippet above, it immediately jumps to the closing parenthesis, for the "sendBackToMenuCmd" method, without executing any of the return code. This is inexplicable behaviour. I am presuming that this is the same for the other three Sub Models. Does anyone know if there is any type of fix for it, as I have tried everything and anything imaginable?