spf13/cobraApache-2.0adbc881Report / request removal

Command Execution Flow

Calling rootCmd.Execute() starts a root-level dispatch process: Cobra finds the command represented by the arguments, parses its flags, validates its positional arguments, and invokes the selected command’s lifecycle. Execute() itself delegates to ExecuteC(), which returns the selected command together with any error.

This flow exists to give commands predictable extension points. Global initialization and finalization surround the selected command, while persistent hooks can be inherited from parents and optionally traversed across the full command path.

Sources: command.go:1070-1073, command.go:1084-1149, cobra.go:99-101, cobra.go:105-107, command.go:54-260, command.go:905-1045

Core concepts

Root execution

The root execution entry point is the command on which Cobra performs dispatch, even when the caller invokes Execute() on a child. ExecuteC() redirects child execution to Root().ExecuteC().

Sources: command.go:1084-1092

Selected command

The selected command is the command returned by Find or Traverse after Cobra interprets the argument list and separates flags from command names.

Sources: command.go:1119-1124, command.go:821-860

Lifecycle hook

A lifecycle hook is a function field on Command that runs before, during, or after the selected command’s main Run or RunE function.

Sources: command.go:117-145

Global hook

A global hook is a function registered through OnInitialize or OnFinalize; Cobra stores these functions and invokes them through preRun and postRun.

The command-level lifecycle has these stages:

StagePlain meaningCode names
Persistent pre-runInherited setup before the command’s local pre-runPersistentPreRunE, PersistentPreRun
Local pre-runSetup belonging only to the selected commandPreRunE, PreRun
Main runThe command’s workRunE, Run
Local post-runCleanup belonging only to the selected commandPostRunE, PostRun
Persistent post-runInherited cleanup after the local post-runPersistentPostRunE, PersistentPostRun

Sources: cobra.go:99-101, cobra.go:105-107, command.go:1047-1051, command.go:1053-1057, command.go:117-145

How root execution selects and prepares a command

Execute() calls ExecuteC(). ExecuteC() supplies a background context when necessary, redirects child invocations to the root, initializes the default help command and completion commands, checks command groups, and chooses either Traverse or Find depending on TraverseChildren.

The following sequence answers what happens from rootCmd.Execute() until the selected command begins its lifecycle.

Dispatch to the selected command — How does Execute reach the leaf command?

Evidence

Find recursively identifies subcommands after stripping flags, while Traverse preserves flag handling during recursive traversal and parses accumulated flags before descending.

After selection, ExecuteC() passes the remaining flags to cmd.execute. The selected command receives the root context when it does not already have one, and execution errors are handled after cmd.execute returns.

Inside execute, Cobra initializes default help and version flags, parses flags, handles explicit help or version requests, and stops with help when the command is not runnable. It then runs the command lifecycle. A command is runnable when either Run or RunE is present.

Sources: command.go:1070-1073, command.go:1084-1124, command.go:757-778, command.go:821-860, command.go:1137-1169, command.go:914-961, command.go:1596-1598

How hooks run around the leaf command

Before command-specific hooks, execute calls preRun; a deferred postRun is installed immediately afterward. preRun invokes every function registered in initializers, and postRun invokes every function registered in finalizers. Because postRun is deferred, finalizers belong around the command execution rather than inside the command’s local hook chain.

The lifecycle then validates positional arguments and constructs the parent list used for persistent hooks. With traversal disabled, the list is arranged so Cobra stops after the first applicable persistent hook; with traversal enabled, it is arranged from the root toward the selected command.

Hook ordering — Which hooks run before and after the selected command?

Evidence

For each persistent pre-run slot, Cobra prefers the error-returning form, such as PersistentPreRunE, and otherwise calls the non-error form, such as PersistentPreRun. A returned error stops the lifecycle before the next stage. The selected command’s local pre-run follows the same error-first rule for PreRunE and PreRun.

The documented order is persistent pre-run, local pre-run, run, local post-run, and persistent post-run. Local pre-run and post-run hooks run only when the current command declares its run function.

Sources: command.go:959-961, command.go:1047-1051, command.go:1053-1057, command.go:963-983, command.go:984-1005, command.go:54-260

How parent traversal changes persistent hooks

By default, EnableTraverseRunHooks is false. In that mode, Cobra executes only the first applicable persistent pre-run hook while walking from the selected command toward its parents; the documented test order is the child persistent pre-run, child local pre-run, child run, child local post-run, and child persistent post-run.

When EnableTraverseRunHooks is true, Cobra collects the command path from the root parent to the selected command for persistent pre-runs, and persistent post-runs run from the selected command back toward the root. The documented test order therefore places the parent persistent pre-run before the child persistent pre-run, and the parent persistent post-run after the child persistent post-run.

OnInitialize only registers functions; it does not execute them immediately. The functions are appended to initializers, then invoked by preRun at the start of each command execution. OnFinalize follows the same pattern with finalizers and postRun.

Sources: command.go:972-997, command_test.go:1709-1716, command.go:972-983, command_test.go:1697-1707, cobra.go:99-101, cobra.go:105-107, command.go:1047-1051, command.go:1053-1057

How it connects

Command selection depends on the command tree and parent links established by command registration; see Command Tree & Registration. Find, Traverse, Parent, and Root are the execution-time hand-offs between that tree and the lifecycle.

Flag merging and parsing happen before positional argument validation and hook execution; see Flag Parsing & Persistent Flags. ParseFlags merges persistent flags before parsing the selected command’s arguments.

Argument validation is the boundary immediately before persistent and local pre-run hooks; see Positional Argument Validators. ValidateArgs delegates to the command’s Args validator when one is configured.

Sources: command.go:821-860, command.go:1892-1894, command.go:892-897, command.go:1868-1888, command.go:1898-1902, command.go:1172-1177

Key takeaways

  • Execute() delegates to root-level ExecuteC(), which selects a command before calling execute.
  • The normal lifecycle order is persistent pre-run, pre-run, run, post-run, then persistent post-run.
  • EnableTraverseRunHooks controls whether parent persistent hooks are included across the command path.
  • OnInitialize and OnFinalize register global hooks that execute through preRun and deferred postRun.

Sources: command.go:1070-1073, command.go:1084-1149, command.go:54-260, command.go:972-997, cobra.go:99-101, cobra.go:105-107, command.go:959-961

Want this for your repos?

Try Angada AI Wiki