kovidgoyal/kitty · info · ErrExec
Execute command
Error message
Execute command
What it means
ErrExec is a sentinel error in tools/cmd/at/shell.go declared with the message 'Execute command'; it is returned by shell_main when the interactive shell loop should finish by executing a command. The message is a label for the control-flow signal ('we have a command, now execute it'), not a diagnostic: `kitten`'s @ shell uses readline to build a command line and uses this error to unwind so the caller can os.exec the resulting command.
Source
Thrown at tools/cmd/at/shell.go:28
"path/filepath"
"strings"
"time"
"github.com/kovidgoyal/kitty/tools/cli"
"github.com/kovidgoyal/kitty/tools/cli/markup"
"github.com/kovidgoyal/kitty/tools/tui/loop"
"github.com/kovidgoyal/kitty/tools/tui/readline"
"github.com/kovidgoyal/kitty/tools/utils"
"github.com/kovidgoyal/kitty/tools/utils/shlex"
)
var _ = fmt.Print
var formatter *markup.Context
const prompt = "🐱 "
var ErrExec = errors.New("Execute command")
func shell_loop(rl *readline.Readline, kill_if_signaled bool) (int, error) {
lp, err := loop.New(loop.NoAlternateScreen, loop.NoRestoreColors)
if err != nil {
return 1, err
}
rl.ChangeLoopAndResetText(lp)
lp.OnInitialize = func() (string, error) {
rl.Start()
return "", nil
}
lp.OnFinalize = func() string { rl.End(); return "" }
lp.OnResumeFromStop = func() error {
rl.Start()
return nil
}View on GitHub (pinned to 6d5d0c4406)
Solutions
- Recognize it as a sentinel: compare with errors.Is(err, ErrExec) and handle it by executing the pending command, not by reporting failure
- If writing a custom loop around shell_loop, mirror the upstream shell_main handling of this return value
- For debugging, read tools/cmd/at/shell.go's shell_main to see the intended control flow
Example fix
// before
if err != nil { return err } // bubbles 'Execute command' up as a failure
// after
if errors.Is(err, ErrExec) {
// execute the command accumulated by the readline loop
}
if err != nil { return err } Defensive patterns
Strategy: type-guard
Type guard
func isExecSignal(err error) bool {
return errors.Is(err, atshell.ErrExec)
} Try / catch
err := shell_main(...)
if errors.Is(err, atshell.ErrExec) {
// execute the accumulated command; this is success, not failure
} else if err != nil {
return err
} Prevention
- Learn the sentinel's role in shell_main before wrapping the loop
- Never stringify-and-log this error blindly; always errors.Is it
- Keep upstream control-flow handling when embedding the at shell
When it happens
Trigger: Running the interactive shell component of the `at` tool (shell_main/shell_loop): once the user submits a command, the loop returns ErrExec to signal the outer code to execute the collected command line.
Common situations: Almost never seen by end users; developers embedding or testing the at-shell loop who mishandle the sentinel as a real error; grepping error strings and being confused that the message looks like a user-facing failure.
Related errors
- Canceled by user
- No lexer available for this format
- No cache found and max cache age is negative
- Too much piped data
- This must be run as kitten ask
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/cc99f89ebbf31c43.
Report an issue: GitHub.