Cobra is a library for building modern command-line interface (CLI) applications in Go. It powers the command structure behind tools such as kubectl, hugo, the GitHub CLI (gh), and the go toolchain's cousins. If you are writing a Go program that needs subcommands, flags, automatic help text, shell completions, and generated documentation, Cobra gives you all of it from a small amount of declarative code.
This guide is written for the developer who consumes Cobra — you import it into your Go program and use its API. It shows you how to install Cobra, define commands, wire up flags and arguments, control the execution lifecycle, and ship polished extras like shell completion and man pages. Every command, field, function, and environment variable described here comes directly from the Cobra source.
Note
Cobra is a library, not a standalone program. You do not "run Cobra" — you build a Go binary that imports Cobra. Throughout this guide, "your program" or "your CLI" means the application you are building, and
appis used as a placeholder for its name.
Table of Contents
- Quick Start
- Installation & Requirements
- Core Concepts
- Defining Commands
- Working with Flags
- Validating Positional Arguments
- The Run Lifecycle (Hooks)
- Organizing Commands
- Shell Completions
- Active Help
- Generating Documentation
- Versioning, Help, and Error Output
- Global Behavior Toggles
- Common Recipes
- Troubleshooting
- FAQ
- Quick Reference
1. Quick Start
A minimal Cobra program is a single root command that you execute from main.
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
func main() {
rootCmd := &cobra.Command{
Use: "hello",
Short: "Hello prints a friendly greeting",
Run: func(cmd *cobra.Command, args []string) {
fmt.Fprintln(cmd.OutOrStdout(), "Hello, world!")
},
}
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
Build and run it:
go run .
Hello, world!
Ask for help — Cobra wires this up automatically:
go run . --help
Hello prints a friendly greeting
Usage:
hello [flags]
Flags:
-h, --help help for hello
Tip
You never have to write a
--helpflag or a usage message yourself. Cobra adds the-h, --helpflag to every command automatically the first time it executes (see InitDefaultHelpFlag).
That is the whole loop: build a *cobra.Command, give it a Run function, and call Execute(). Everything else in this guide is about adding structure — subcommands, flags, validation, and polish — on top of this foundation.
2. Installation & Requirements
Requirements
| Requirement | Detail |
|---|---|
| Go version | Go 1.15 or newer (declared in Cobra's go.mod) |
| Module mode | Go modules must be enabled (the default since Go 1.16) |
| Platforms | Linux, macOS, Windows (a Windows-specific helper is built in — see mousetrap) |
Cobra pulls in a small set of dependencies automatically when you go get it:
| Dependency | Role |
|---|---|
github.com/spf13/pflag |
POSIX/GNU-style flag parsing. Cobra's flag API is a thin layer over pflag. |
github.com/cpuguy83/go-md2man/v2 |
Renders man pages (used by the doc package). |
github.com/inconshreveable/mousetrap |
Detects when a Windows program was launched from Explorer. |
go.yaml.in/yaml/v3 |
Marshals YAML documentation (used by the doc package). |
Add Cobra to your project
New project
mkdir myapp && cd myapp
go mod init github.com/you/myapp
go get github.com/spf13/cobra@latest
Existing module
go get github.com/spf13/cobra@latest
Then import it in your code:
import "github.com/spf13/cobra"
Note
Cobra ships a companion generator tool (
cobra-cli) in a separate repository for scaffolding a command tree. It is optional — this guide builds everything by hand so you understand exactly what each piece does. Nothing in this guide requires the generator.
Verify the installation
go build ./...
# (no output on success)
A clean build with no cannot find package errors confirms Cobra and its dependencies resolved correctly.
3. Core Concepts
Cobra is built around three ideas. Understanding them makes the rest of the API obvious.
Command
: A *cobra.Command represents a single action your program can perform — git commit, kubectl get, or your own app serve. Commands nest: a root command has children, which can have their own children. The first word of a command's Use field is its name.
Flag
: A named option such as --verbose or -o json. Flags are parsed by pflag and attached to commands. A flag is either local (applies only to the command it is set on) or persistent (also applies to all descendant commands).
Argument
: A positional value left over after flags are removed — e.g. the main.go in app build main.go. You can validate how many arguments a command accepts and which values are allowed.
A Cobra application is a tree of commands. Execution always starts at the root, walks the tree to find the command that matches the user's input, parses the flags, validates the arguments, and runs the matched command's Run function.
app (root command)
├── serve (subcommand)
├── migrate (subcommand)
│ ├── up (sub-subcommand)
│ └── down (sub-subcommand)
└── version (subcommand)
Important
The first word of
Useis the command's name.Use: "serve [flags]"produces a command namedserve. Cobra derives the name withCommand.Name(), which returns everything up to the first space.
4. Defining Commands
The Command struct
You configure a command by setting fields on a cobra.Command literal. These are the fields you will use most often:
| Field | Type | Purpose |
|---|---|---|
Use |
string |
One-line usage. First word is the command name. |
Aliases |
[]string |
Alternate names that invoke this command. |
Short |
string |
One-line description shown in the parent's command list. |
Long |
string |
Full description shown in this command's own help. |
Example |
string |
Example invocations shown under an Examples: heading. |
Run |
func(cmd *Command, args []string) |
The work function. |
RunE |
func(cmd *Command, args []string) error |
Like Run but returns an error. Prefer this. |
Args |
PositionalArgs |
Argument count/validity rule (see §6). |
ValidArgs |
[]Completion |
Allowed non-flag arguments, offered in completion. |
Version |
string |
Enables a --version flag for this command. |
Deprecated |
string |
Marks the command deprecated; the message prints when used. |
Hidden |
bool |
Hides the command from help output (it still works). |
GroupID |
string |
Groups this subcommand under a heading in the parent's help. |
SuggestFor |
[]string |
Names for which this command is suggested. |
Annotations |
map[string]string |
Arbitrary key/value metadata for your own use. |
Tip
Use
RunErather thanRunin real applications. Returning an error lets Cobra print it consistently and letsExecute()signal failure to yourmain. ReserveRunfor trivial commands that cannot fail.
Usage string syntax
The Use field documents the command's arguments. Cobra recommends this notation (taken from the field's own documentation):
| Notation | Meaning |
|---|---|
[ ] |
An optional argument. Arguments not in brackets are required. |
... |
You may specify multiple values for the previous argument. |
| |
Mutually exclusive — use the left argument or the right, not both. |
{ } |
A set of mutually exclusive arguments, one of which is required. |
Use: "add [-F file | -D dir]... [-f format] profile"
Adding subcommands
Build child commands and attach them with AddCommand:
var rootCmd = &cobra.Command{Use: "app"}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the HTTP server",
RunE: func(cmd *cobra.Command, args []string) error {
return startServer()
},
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number",
Run: func(cmd *cobra.Command, args []string) {
fmt.Fprintln(cmd.OutOrStdout(), "app v1.0.0")
},
}
func init() {
rootCmd.AddCommand(serveCmd, versionCmd)
}
app serve
app version
Note
AddCommandaccepts one or more commands in a single call:rootCmd.AddCommand(serveCmd, versionCmd, migrateCmd). A command may not be added as a child of itself — Cobra panics withCommand can't be a child of itselfif you try.
Aliases and suggestions
var listCmd = &cobra.Command{
Use: "list",
Aliases: []string{"ls", "l"},
SuggestFor: []string{"show"},
Short: "List all items",
Run: func(cmd *cobra.Command, args []string) { /* ... */ },
}
Aliases—app lsandapp lboth invokelist.SuggestFor— if the user typesapp show, Cobra suggestslistin its "Did you mean this?" message.
Cobra automatically suggests near-matches for mistyped commands using Levenshtein distance:
app serv
Error: unknown command "serv" for "app"
Did you mean this?
serve
Run 'app --help' for usage.
The suggestion threshold is controlled by SuggestionsMinimumDistance (default 2). Set DisableSuggestions: true on a command to turn the feature off.
5. Working with Flags
Cobra exposes flags through pflag. You declare flags on a command's flag set inside init() (or before calling Execute).
Local vs. persistent flags
| Method | Scope |
|---|---|
cmd.Flags() |
Local — applies only to this command. |
cmd.PersistentFlags() |
Persistent — applies to this command and all of its children. |
func init() {
// Persistent: available on rootCmd and every subcommand
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "enable verbose logging")
// Local: only valid on serveCmd
serveCmd.Flags().IntP("port", "p", 8080, "port to listen on")
serveCmd.Flags().StringP("host", "H", "localhost", "host to bind to")
}
The four arguments to a typed flag constructor are name, shorthand, default value, and usage string. The P suffix (BoolP, IntP, StringP, …) means "with a shorthand". Without the P, omit the shorthand:
serveCmd.Flags().Bool("tls", false, "enable TLS") // long form only: --tls
Reading flag values
Inside a Run/RunE function, read flags from the command:
RunE: func(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetInt("port")
verbose, _ := cmd.Flags().GetBool("verbose") // works: persistent flag is inherited
host, _ := cmd.Flags().GetString("host")
fmt.Fprintf(cmd.OutOrStdout(), "serving on %s:%d (verbose=%t)\n", host, port, verbose)
return nil
}
Alternatively, bind a flag to a variable with the *Var constructors:
var port int
serveCmd.Flags().IntVarP(&port, "port", "p", 8080, "port to listen on")
// `port` now holds the parsed value after flag parsing
Required flags
Mark a flag required so Cobra errors if it is missing:
serveCmd.Flags().String("config", "", "path to config file")
serveCmd.MarkFlagRequired("config")
Error: required flag(s) "config" not set
For a persistent flag, use MarkPersistentFlagRequired.
Flag groups
Express relationships between flags. Cobra validates them before running the command:
| Method | Rule enforced |
|---|---|
cmd.MarkFlagsRequiredTogether("a", "b") |
If any of these flags is set, all must be set. |
cmd.MarkFlagsOneRequired("a", "b") |
At least one of these flags must be set. |
cmd.MarkFlagsMutuallyExclusive("a", "b") |
At most one of these flags may be set. |
func init() {
serveCmd.Flags().String("tls-cert", "", "TLS certificate file")
serveCmd.Flags().String("tls-key", "", "TLS key file")
serveCmd.MarkFlagsRequiredTogether("tls-cert", "tls-key")
serveCmd.Flags().Bool("json", false, "JSON output")
serveCmd.Flags().Bool("yaml", false, "YAML output")
serveCmd.MarkFlagsMutuallyExclusive("json", "yaml")
}
The exact errors Cobra produces (from source):
if any flags in the group [tls-cert tls-key] are set they must all be set; missing [tls-key]
if any flags in the group [json yaml] are set none of the others can be; [json yaml] were all set
at least one of the flags in the group [a b] is required
Warning
The flags named in a group must already be defined on the command (or be persistent flags reachable from it) when you call the
MarkFlags*method. If a named flag does not exist, Cobra panics — for exampleFailed to find flag "tls-key" and mark it as being required in a flag group. Define flags first, then group them.
Tying file/directory completion to a flag
These helpers tell shell completion how to complete a flag's value:
| Method | Effect |
|---|---|
cmd.MarkFlagFilename("f", "json", "yaml") |
Complete only files ending in the given extensions. |
cmd.MarkFlagDirname("d") |
Complete only directory names. |
serveCmd.Flags().String("config", "", "config file")
serveCmd.MarkFlagFilename("config", "yaml", "yml", "json")
6. Validating Positional Arguments
Set a command's Args field to one of Cobra's built-in validators. They run after flag parsing and before Run.
| Validator | Passes when… |
|---|---|
cobra.NoArgs |
No positional arguments are given. |
cobra.ArbitraryArgs |
Always (any number of arguments). |
cobra.MinimumNArgs(n) |
At least n arguments. |
cobra.MaximumNArgs(n) |
At most n arguments. |
cobra.ExactArgs(n) |
Exactly n arguments. |
cobra.RangeArgs(min, max) |
Between min and max arguments (inclusive). |
cobra.OnlyValidArgs |
Every argument is listed in the command's ValidArgs. |
cobra.NoDuplicateArgs |
No argument value is repeated. |
cobra.MatchAll(v1, v2, …) |
All of the supplied validators pass. |
var deleteCmd = &cobra.Command{
Use: "delete [names...]",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
for _, name := range args {
// args holds the validated positional values
}
return nil
},
}
The exact error messages each validator emits (from source):
| Validator | Error message format |
|---|---|
MinimumNArgs(n) |
requires at least %d arg(s), only received %d |
MaximumNArgs(n) |
accepts at most %d arg(s), received %d |
ExactArgs(n) |
accepts %d arg(s), received %d |
RangeArgs(min,max) |
accepts between %d and %d arg(s), received %d |
NoArgs |
unknown command %q for %q |
OnlyValidArgs |
invalid argument %q for %q |
NoDuplicateArgs |
duplicate argument %q for %q |
Combining validators with MatchAll
MatchAll runs several validators in sequence. Use it to require both a count and a value constraint:
var pickCmd = &cobra.Command{
Use: "pick",
ValidArgs: []string{"red", "green", "blue"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) { /* ... */ },
}
Note
If you leave
Argsunset (nil), Cobra applies legacy behavior: a command with no subcommands accepts arbitrary arguments, while a root command with subcommands rejects unknown commands. SettingArgsexplicitly is recommended for predictable behavior.
Caution
cobra.ExactValidArgs(n)still exists but is deprecated. Usecobra.MatchAll(cobra.ExactArgs(n), cobra.OnlyValidArgs)instead — it is the exact replacement.
7. The Run Lifecycle (Hooks)
A command can define hook functions that run around its main Run. The order is fixed and documented in the Cobra source:
PersistentPreRun/PersistentPreRunEPreRun/PreRunERun/RunEPostRun/PostRunEPersistentPostRun/PersistentPostRunE
Every hook has a plain form and an E form that returns an error. If both are defined on a command, only the E form runs. All hooks receive the same arguments (the positional args after the command name).
var rootCmd = &cobra.Command{
Use: "app",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return loadConfig() // runs before every subcommand
},
}
var serveCmd = &cobra.Command{
Use: "serve",
PreRun: func(cmd *cobra.Command, args []string) { openLogFile() },
RunE: func(cmd *cobra.Command, args []string) error { return serve() },
PostRun: func(cmd *cobra.Command, args []string) { closeLogFile() },
}
Inheritance of persistent hooks
PersistentPreRun(E) and PersistentPostRun(E) are inherited by children — but with an important default: only the first one found while walking up the tree executes. If both the root and a subcommand define PersistentPreRunE, only the subcommand's runs.
Important
To run every persistent hook from the root down to the executed command (and every persistent post-run back up), set the package variable
cobra.EnableTraverseRunHooks = true. With it off (the default), inner persistent hooks override outer ones rather than chaining.
Warning
PersistentPostRun(E)andPostRun(E)do not run ifRunE(orPreRunE) returns an error — execution stops at the failing hook. This is a frequent source of confusion when using post-run hooks for cleanup; put unconditional cleanup in adeferinsideRunEinstead, or handle it explicitly.
Per-command initializers
For setup that should run on every command's Execute, register a function with OnInitialize; for teardown, use OnFinalize:
func init() {
cobra.OnInitialize(initLogging)
cobra.OnFinalize(flushLogging)
}
8. Organizing Commands
Grouping subcommands in help
When a command has many children, group them under headings. Define a group on the parent, then set each child's GroupID:
func init() {
rootCmd.AddGroup(&cobra.Group{ID: "ops", Title: "Operations:"})
rootCmd.AddGroup(&cobra.Group{ID: "info", Title: "Information:"})
serveCmd.GroupID = "ops"
migrateCmd.GroupID = "ops"
versionCmd.GroupID = "info"
rootCmd.AddCommand(serveCmd, migrateCmd, versionCmd)
}
Operations:
migrate Run database migrations
serve Start the HTTP server
Information:
version Print the version number
Caution
If you set a
GroupIDon a child but never register that group on the parent withAddGroup, Cobra panics at startup:group id 'ops' is not defined for subcommand 'app serve'. Always add the group before assigning its ID.
Additional help topics
A command with a Short/Long description but no Run function and no runnable children becomes an additional help topic — it appears under "Additional help topics" in help output and can be read with app help <topic> but cannot be executed.
Controlling output streams
Cobra commands write through accessor methods so you can redirect output (useful in tests):
| Method | Returns |
|---|---|
cmd.OutOrStdout() |
The configured out writer, or os.Stdout. |
cmd.ErrOrStderr() |
The configured err writer, or os.Stderr. |
cmd.InOrStdin() |
The configured in reader, or os.Stdin. |
Set them with cmd.SetOut(w), cmd.SetErr(w), and cmd.SetIn(r). There are also convenience print helpers that honor these streams: cmd.Print, cmd.Println, cmd.Printf, cmd.PrintErr, cmd.PrintErrln, cmd.PrintErrf.
Tip
Always write through
cmd.OutOrStdout()instead offmt.Println. It makes your command testable (you can capture output withcmd.SetOut(buf)) and lets a parent command redirect a child's output.
9. Shell Completions
This is one of Cobra's signature features. Any Cobra program automatically gains a completion command that emits a completion script for bash, zsh, fish, or PowerShell — you do not write any of it.
app completion --help
Generate the autocompletion script for app for the specified shell.
See each sub-command's help for details on how to use the generated script.
Usage:
app completion [command]
Available Commands:
bash Generate the autocompletion script for bash
fish Generate the autocompletion script for fish
powershell Generate the autocompletion script for powershell
zsh Generate the autocompletion script for zsh
Enabling completion for your shell
The Long help of each subcommand contains exact, copy-pasteable instructions. The essentials:
bash
# current session
source <(app completion bash)
# every session (Linux)
app completion bash > /etc/bash_completion.d/app
# every session (macOS, Homebrew)
app completion bash > $(brew --prefix)/etc/bash_completion.d/app
zsh
# enable completion once, if not already enabled
echo "autoload -U compinit; compinit" >> ~/.zshrc
# current session
source <(app completion zsh)
# every session (Linux)
app completion zsh > "${fpath[1]}/_app"
# every session (macOS, Homebrew)
app completion zsh > $(brew --prefix)/share/zsh/site-functions/_app
fish
# current session
app completion fish | source
# every session
app completion fish > ~/.config/fish/completions/app.fish
powershell
# current session
app completion powershell | Out-String | Invoke-Expression
# every session: add the above line's output to your PowerShell profile
Note
The bash script depends on the
bash-completionpackage. If completion does not work in bash, installbash-completionvia your OS package manager and start a new shell.
Completing your command's arguments
Out of the box, completion suggests subcommand names, flag names, and required flags. To complete argument values, provide them via ValidArgs (static) or ValidArgsFunction (dynamic).
Static — a fixed list:
var setCmd = &cobra.Command{
Use: "set-level",
ValidArgs: []string{"debug", "info", "warn", "error"},
Args: cobra.OnlyValidArgs,
Run: func(cmd *cobra.Command, args []string) { /* ... */ },
}
Dynamic — computed at completion time:
var openCmd = &cobra.Command{
Use: "open",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
// e.g. list open projects matching `toComplete`
return []cobra.Completion{"alpha", "beta"}, cobra.ShellCompDirectiveNoFileComp
},
Run: func(cmd *cobra.Command, args []string) { /* ... */ },
}
Add descriptions to completions with cobra.CompletionWithDesc("choice", "description") (zsh and fish display the description).
Completing flag values
Register a completion function for a specific flag with RegisterFlagCompletionFunc:
serveCmd.Flags().String("region", "", "deployment region")
serveCmd.RegisterFlagCompletionFunc("region",
func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
return []cobra.Completion{"us-east-1", "eu-west-1"}, cobra.ShellCompDirectiveNoFileComp
})
Warning
RegisterFlagCompletionFuncreturns an error if the flag does not exist (RegisterFlagCompletionFunc: flag '...' does not exist) or if you register the same flag twice (... already registered). Register completion only after the flag is defined, and only once per flag.
Completion directives
A completion function returns a ShellCompDirective bitmask that tells the shell how to behave. Combine them with |.
| Directive | Effect |
|---|---|
ShellCompDirectiveDefault |
Let the shell do its default (file) completion. The zero value. |
ShellCompDirectiveNoSpace |
Do not append a space after the completion. |
ShellCompDirectiveNoFileComp |
Do not fall back to file completion. |
ShellCompDirectiveFilterFileExt |
Treat the returned values as file-extension filters. |
ShellCompDirectiveFilterDirs |
Complete directory names only. |
ShellCompDirectiveKeepOrder |
Preserve the order you returned completions in (don't sort). |
ShellCompDirectiveError |
Signal an error; completions are ignored. |
Two ready-made completion functions cover common cases:
cobra.NoFileCompletions— disables file completion for a command/flag that takes no completable value.cobra.FixedCompletions(choices, directive)— always returns the same list.
cmd.ValidArgsFunction = cobra.NoFileCompletions
Controlling the generated completion command
Customize or disable the built-in completion command via the root command's CompletionOptions:
| Field | Effect |
|---|---|
DisableDefaultCmd |
Do not create the completion command at all. |
DisableNoDescFlag |
Do not add the --no-descriptions flag. |
DisableDescriptions |
Turn off completion descriptions entirely. |
HiddenDefaultCmd |
Keep the completion command but hide it from help. |
rootCmd.CompletionOptions.DisableDefaultCmd = true
10. Active Help
Active Help lets you push contextual messages to users during completion — for example, a hint when no completions are available. Build the messages onto your completion slice with AppendActiveHelp:
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]cobra.Completion, cobra.ShellCompDirective) {
var comps []cobra.Completion
if len(args) == 0 {
comps = cobra.AppendActiveHelp(comps, "You must specify a project name")
}
return comps, cobra.ShellCompDirectiveNoFileComp
},
Users (or you) can disable Active Help through environment variables, read at completion time:
| Variable | Effect |
|---|---|
COBRA_ACTIVE_HELP=0 |
Globally disable Active Help for any Cobra program. |
<PROGRAM>_ACTIVE_HELP=0 |
Disable Active Help for your program specifically. <PROGRAM> is your root command's name, upper-cased, with non-alphanumeric characters replaced by _. |
Note
The
<PROGRAM>prefix is derived fromrootCmd.Name(). A program namedmy-appreadsMY_APP_ACTIVE_HELP.
11. Generating Documentation
The github.com/spf13/cobra/doc subpackage turns your command tree into documentation files. It supports four formats, each with a single-file and a whole-tree generator.
| Format | Tree generator | Single-command generator |
|---|---|---|
| Man pages | doc.GenManTree(cmd, header, dir) |
doc.GenMan(cmd, header, w) |
| Markdown | doc.GenMarkdownTree(cmd, dir) |
doc.GenMarkdown(cmd, w) |
| reStructuredText | doc.GenReSTTree(cmd, dir) |
doc.GenReST(cmd, w) |
| YAML | doc.GenYamlTree(cmd, dir) |
doc.GenYaml(cmd, w) |
Generate a full Markdown tree:
package main
import (
"log"
"github.com/spf13/cobra/doc"
)
func main() {
if err := doc.GenMarkdownTree(rootCmd, "./docs"); err != nil {
log.Fatal(err)
}
}
Generate man pages with a header:
header := &doc.GenManHeader{
Title: "APP",
Section: "1",
Source: "App 1.0",
}
if err := doc.GenManTree(rootCmd, header, "./man"); err != nil {
log.Fatal(err)
}
Each generator has a *Custom variant (GenMarkdownTreeCustom, GenManTreeFromOpts, GenReSTTreeCustom, GenYamlTreeCustom) that accepts a filePrepender and linkHandler so you can add front-matter or rewrite cross-links.
Note
By default the generated docs end with an "Auto generated by spf13/cobra" tag (and man pages add a
HISTORYsection). SetDisableAutoGenTag: trueon a command to omit it.
Warning
The tree generators may produce ambiguous filenames if your command names contain
-. As the source notes: with acmdthat has subcommandssubandsub-third, wheresubalso has a childthird, it is undefined which output lands incmd-sub-third. Avoid-in command names if you generate per-command files.
12. Versioning, Help, and Error Output
Version flag
Set the Version field on your root command and Cobra adds a --version flag (and a -v shorthand, if free):
var rootCmd = &cobra.Command{
Use: "app",
Version: "1.4.2",
}
app --version
app version 1.4.2
Customize the format with rootCmd.SetVersionTemplate(...).
Note
The
--versionflag andversiontemplate are tied to whichever command has a non-emptyVersion. A known limitation (community-reported) is that--versiondoes not propagate to subcommands automatically — setVersionon the specific command if you wantapp sub --versionto work.
Help
Cobra adds a -h, --help flag to every command and a help command to any command that has subcommands. Override the default behavior with cmd.SetHelpFunc(...), cmd.SetHelpTemplate(...), cmd.SetUsageFunc(...), or cmd.SetUsageTemplate(...).
Quieting output
| Field | Effect |
|---|---|
SilenceErrors: true |
Cobra does not print errors returned from RunE (you handle them). |
SilenceUsage: true |
Cobra does not print the usage string after an error. |
var rootCmd = &cobra.Command{
Use: "app",
SilenceUsage: true, // don't dump usage on every runtime error
SilenceErrors: false,
}
Tip
Setting
SilenceUsage: trueis almost always what you want once your CLI is stable. Without it, every runtime error (e.g. "file not found") prints the full usage text, which is noise for users who already know the command.
Custom error prefix
The default error prefix is Error:. Change it with cmd.SetErrPrefix("myapp:").
Helper: CheckErr
cobra.CheckErr(err) prints Error: <msg> to stderr and exits with code 1 if err is non-nil — a concise replacement for the if err != nil { ... os.Exit(1) } boilerplate in small programs.
func main() {
cobra.CheckErr(rootCmd.Execute())
}
13. Global Behavior Toggles
These are package-level variables in the cobra package. Set them once (typically in init()) to change behavior program-wide.
| Variable | Default | Effect |
|---|---|---|
EnablePrefixMatching |
false |
Allow invoking a command by an unambiguous prefix of its name. |
EnableCommandSorting |
true |
Sort commands alphabetically in help. Set false to keep insertion order. |
EnableCaseInsensitive |
false |
Match command names and aliases case-insensitively. |
EnableTraverseRunHooks |
false |
Run all persistent hooks along the command path (see §7). |
MousetrapHelpText |
(a message) | Text shown on Windows when the program is launched from Explorer. Set to "" to disable the splash. |
MousetrapDisplayDuration |
5 * time.Second |
How long that Windows message shows. 0 waits for the Enter key. |
func init() {
cobra.EnableCommandSorting = false
cobra.EnablePrefixMatching = true
}
Caution
Cobra's own documentation warns that
EnablePrefixMatching"can be a dangerous thing to automatically enable in CLI tools" — a new command added later could make a previously-unambiguous prefix ambiguous, silently changing behavior. Enable it deliberately.
Note
The mousetrap feature only affects Windows binaries launched by double-clicking in Explorer. It shows the help text so users realize the program is a command-line tool. It has no effect on Linux or macOS.
14. Common Recipes
A complete multi-command program
package main
import (
"fmt"
"os"
"github.com/spf13/cobra"
)
var verbose bool
var rootCmd = &cobra.Command{
Use: "app",
Short: "App does useful things",
Version: "1.0.0",
SilenceUsage: true,
}
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the server",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetInt("port")
fmt.Fprintf(cmd.OutOrStdout(), "listening on :%d\n", port)
return nil
},
}
func init() {
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
serveCmd.Flags().IntP("port", "p", 8080, "port to listen on")
rootCmd.AddCommand(serveCmd)
}
func main() {
if err := rootCmd.Execute(); err != nil {
os.Exit(1)
}
}
Pass a context into commands
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := rootCmd.ExecuteContext(ctx); err != nil {
os.Exit(1)
}
}
// inside a Run/RunE function:
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context() // the context passed to ExecuteContext
return doWork(ctx)
}
Testing a command
func TestServe(t *testing.T) {
buf := new(bytes.Buffer)
rootCmd.SetOut(buf)
rootCmd.SetArgs([]string{"serve", "--port", "9000"})
if err := rootCmd.Execute(); err != nil {
t.Fatal(err)
}
if !strings.Contains(buf.String(), ":9000") {
t.Errorf("unexpected output: %q", buf.String())
}
}
SetArgs overrides os.Args[1:], and SetOut/SetErr capture output — together they make commands fully testable without spawning a process.
15. Troubleshooting
Error: required flag(s) "X" not set
Cause: A flag marked with MarkFlagRequired (or required as part of a one-required group) was not provided.
Solution: Pass the flag, e.g. app serve --config app.yaml.
Prevention: Document required flags in the command's Long/Example.
Error: unknown command "X" for "app"
Cause: The user typed a name that matches no subcommand or alias. Often a typo.
Solution: Run app --help to list valid commands; Cobra usually prints a "Did you mean this?" suggestion.
Prevention: Add common misspellings to a command's SuggestFor, or Aliases for legitimate alternates.
Error: if any flags in the group [a b] are set they must all be set; missing [b]
Cause: You used MarkFlagsRequiredTogether("a", "b") and supplied only one of them.
Solution: Provide all flags in the group, or none.
Prevention: Group only flags that are genuinely interdependent.
Error: if any flags in the group [a b] are set none of the others can be; [a b] were all set
Cause: MarkFlagsMutuallyExclusive("a", "b") and the user set more than one.
Solution: Pass at most one of the mutually exclusive flags.
Panic: group id 'X' is not defined for subcommand 'app Y'
Cause: A subcommand has a GroupID whose group was never registered on the parent with AddGroup.
Solution: Call parent.AddGroup(&cobra.Group{ID: "X", Title: "..."}) before adding the child.
Prevention: Define groups and assign GroupIDs in the same init().
Panic: Command can't be a child of itself
Cause: You passed a command to its own AddCommand.
Solution: Build distinct command values; don't add rootCmd to rootCmd.
PersistentPostRun / PostRun never runs
Cause: Post-run hooks are skipped when RunE (or an earlier *RunE) returns an error. (Community issue #1893.)
Solution: Put unconditional cleanup in a defer inside RunE, not in a post-run hook.
Shell completion does nothing in bash
Cause: The bash-completion package is not installed, or the script was not sourced.
Solution: Install bash-completion, then source <(app completion bash) and start a new shell.
Completion debugging
Cause: You want to see why a custom completion function returns nothing.
Solution: Set the BASH_COMP_DEBUG_FILE environment variable to a writable path; Cobra (and your calls to cobra.CompDebugln) append debug logs there during completion.
export BASH_COMP_DEBUG_FILE=/tmp/app-comp.log
--version works on the root but not a subcommand
Cause: The version flag is added only to the command whose Version field is non-empty. (Community issue #1860.)
Solution: Set Version on the specific subcommand if you need app sub --version.
16. FAQ
Q: Is Cobra a program I install and run, or a library?
It is a Go library. You import github.com/spf13/cobra and build your own binary with it. There is no cobra executable to run your commands.
Q: Do I have to use Run or can I return errors?
Use RunE, which returns an error. Cobra prints the error consistently and Execute() returns it so your main can set the exit code. Run (no error) is fine only for commands that cannot fail.
Q: What is the difference between Flags() and PersistentFlags()?
Flags() defines a flag local to one command. PersistentFlags() defines a flag that is also inherited by all of that command's descendants. Use persistent flags for cross-cutting options like --verbose on the root.
Q: How do I make a flag required?
cmd.MarkFlagRequired("name") for a local flag, or cmd.MarkPersistentFlagRequired("name") for a persistent one. Cobra then errors if it is missing.
Q: How do positional arguments differ from flags?
Flags are named (--port 80); positional arguments are the leftover values after flags are removed. Validate their count/values with the Args field (e.g. cobra.ExactArgs(1)).
Q: Where does my command read its arguments?
From the args []string parameter of Run/RunE — it contains the validated positional arguments after the command name and flags.
Q: How do I add tab completion?
You don't have to write any scripts — every Cobra program already has a completion command for bash/zsh/fish/PowerShell. To complete argument or flag values, supply ValidArgs, a ValidArgsFunction, or RegisterFlagCompletionFunc.
Q: Can I generate man pages or Markdown docs from my commands?
Yes. Import github.com/spf13/cobra/doc and call GenManTree, GenMarkdownTree, GenReSTTree, or GenYamlTree.
Q: In what order do the Run hooks fire?
PersistentPreRun → PreRun → Run → PostRun → PersistentPostRun. The E variant takes precedence over the non-E form when both are set.
Q: Why do my root command's persistent pre-run hooks stop firing when a subcommand also defines one?
By default only the first persistent hook found while walking the tree runs. Set cobra.EnableTraverseRunHooks = true to run all of them along the path.
Q: How do I stop Cobra from printing usage on every error?
Set SilenceUsage: true (and optionally SilenceErrors: true to handle error printing yourself).
Q: How do I pass a context.Context into my commands?
Call rootCmd.ExecuteContext(ctx) and read it inside a command with cmd.Context().
Q: Can I invoke a command by a shortened name?
Set cobra.EnablePrefixMatching = true to allow unambiguous prefixes. Use with care — adding commands later can make a prefix ambiguous.
17. Quick Reference
Execution entry points
| Call | Purpose |
|---|---|
cmd.Execute() |
Run the command tree from the root using os.Args[1:]. |
cmd.ExecuteContext(ctx) |
Execute with a context attached. |
cmd.ExecuteC() |
Execute and also return the matched command. |
cobra.CheckErr(err) |
Print Error: ... and os.Exit(1) if err != nil. |
Defining structure
| Call | Purpose |
|---|---|
parent.AddCommand(child...) |
Attach subcommands. |
parent.AddGroup(&cobra.Group{...}) |
Register a help group. |
cmd.SetArgs([]string{...}) |
Override the args (testing). |
cmd.SetOut/SetErr/SetIn(...) |
Redirect I/O streams. |
Flags
| Call | Purpose |
|---|---|
cmd.Flags() |
Local flag set. |
cmd.PersistentFlags() |
Inherited flag set. |
cmd.MarkFlagRequired("f") |
Require a flag. |
cmd.MarkFlagsRequiredTogether(...) |
All-or-none flag group. |
cmd.MarkFlagsOneRequired(...) |
At-least-one flag group. |
cmd.MarkFlagsMutuallyExclusive(...) |
At-most-one flag group. |
cmd.MarkFlagFilename("f", exts...) |
Complete files by extension. |
cmd.MarkFlagDirname("d") |
Complete directories. |
Argument validators
NoArgs · ArbitraryArgs · MinimumNArgs(n) · MaximumNArgs(n) · ExactArgs(n) · RangeArgs(min,max) · OnlyValidArgs · NoDuplicateArgs · MatchAll(...)
Completion
| Call | Purpose |
|---|---|
cmd.ValidArgsFunction = fn |
Dynamic argument completion. |
cmd.RegisterFlagCompletionFunc("f", fn) |
Flag-value completion. |
cobra.NoFileCompletions |
Disable file completion. |
cobra.FixedCompletions(choices, dir) |
Always return the same list. |
cobra.AppendActiveHelp(comps, msg) |
Add an Active Help message. |
Documentation generators (doc package)
GenManTree · GenMarkdownTree · GenReSTTree · GenYamlTree (each with a *Custom variant)
Environment variables (consumed at runtime)
| Variable | Effect |
|---|---|
BASH_COMP_DEBUG_FILE |
Path for completion debug logs. |
COBRA_ACTIVE_HELP=0 |
Disable Active Help globally. |
<PROGRAM>_ACTIVE_HELP=0 |
Disable Active Help for your program. |
<PROGRAM>_COMPLETION_DESCRIPTIONS=false |
Turn off completion descriptions. |