wavetermdev/waveterm · error
no controller found for block %s
Error message
no controller found for block %s
What it means
SendInput looks up a registered controller for the given blockId before forwarding input. If no controller is registered in the in-memory registry for that block (never started, already shut down, or process restarted), it returns 'no controller found for block %s'. Input cannot be delivered without a live controller.
Source
Thrown at pkg/blockcontroller/blockcontroller.go:327
}
connOpts, parseErr := remote.ParseOpts(connName)
if parseErr != nil {
return
}
sshConn := conncontroller.MaybeGetConn(connOpts)
if sshConn != nil {
monitor := sshConn.GetMonitor()
if monitor != nil {
monitor.NotifyInput()
}
}
}
func SendInput(blockId string, inputUnion *BlockInputUnion) error {
controller := getController(blockId)
if controller == nil {
return fmt.Errorf("no controller found for block %s", blockId)
}
sendConnMonitorInputNotification(controller)
return controller.SendInput(inputUnion)
}
// only call this on shutdown
func StopAllBlockControllersForShutdown() {
controllers := getAllControllers()
for blockId, controller := range controllers {
status := controller.GetRuntimeStatus()
if status != nil && status.ShellProcStatus == Status_Running {
go func(id string, c Controller) {
c.Stop(true, Status_Done, false)
wstore.DeleteRTInfo(waveobj.MakeORef(waveobj.OType_Block, id))
}(blockId, controller)
}
}
}View on GitHub (pinned to a4447c1563)
Solutions
- Call ResyncController for the blockId first so the controller is created, then retry SendInput
- Check GetRuntimeStatus / controller registration before sending input
- Refresh the frontend block state so it stops sending input to stale blocks
- If this happens at startup, gate input events until block controllers are resynced
Example fix
// before
SendInput(blockId, input) // controller may not exist
// after
if err := ResyncController(ctx, tabId, blockId, rtOpts, false); err == nil {
SendInput(blockId, input)
} Defensive patterns
Strategy: validation
Validate before calling
func controllerReady(blockId string) bool { return getController(blockId) != nil } Try / catch
if err := SendInput(blockId, input); err != nil && strings.Contains(err.Error(), "no controller found") {
// resync then retry
} Prevention
- Resync controllers after backend restarts before accepting input
- Drop queued input events for closed blocks
- Gate keyboard handlers on controller runtime status
When it happens
Trigger: Calling SendInput (via ControllerInputCommand) for a blockId whose controller was never created via ResyncController, or whose controller was unregistered on shutdown/cleanup.
Common situations: Typing into a terminal block after the backend restarted while the frontend still shows the old block; sending input to a block before its controller finished starting; racing a block close with input events.
Related errors
- no job attached to controller
- no shell input chan
- no workspaces found
- chat not found: %s
- function call with callId %s not found in chat %s
AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01).
Data as JSON: /api/errors/87b744f944744b8b.
Report an issue: GitHub.