gitbutlerapp/gitbutler · error
all help topics have clap command metadata
Error message
all help topics have clap command metadata
What it means
Invariant panic while printing 'but help <topic>': the code walks the clap command tree (Args::command() -> 'help' -> subcommand named after the topic) and find_subcommand_mut returned None. Every HelpTopic variant must have a same-named clap subcommand registered under 'help'; the panic means the enum and the clap definition drifted apart - a topic added or renamed on one side only.
Source
Thrown at crates/but/src/command/help.rs:52
Some(topic) => print_topic(out, topic),
None => print_grouped(out),
}
}
pub fn print_grouped(out: &mut OutputChannel) -> std::fmt::Result {
let allow_truncation = out.format().allows_truncation();
print_grouped_with_truncation(out, allow_truncation)
}
fn print_topic(out: &mut OutputChannel, topic: HelpTopic) -> std::fmt::Result {
use clap::CommandFactory;
use std::fmt::Write;
let mut cmd = Args::command();
let topic_command = cmd
.find_subcommand_mut("help")
.and_then(|help| help.find_subcommand_mut(topic.name()))
.expect("all help topics have clap command metadata");
let t = theme::get();
writeln!(out, "{}", t.important.paint(topic.title()))?;
writeln!(out)?;
// We can't easily hook into Clap's colorize choice. It's only implemented in
// `Command::print_long_help()` and that forces use of `std::io::Stdout`, side-stepping our
// OutputChannel implementation.
//
// A full implementation here would entail using `anstream::AutoStream` along with
// `Command::get_color()` and map that to `anstream::ColorChoice`. But just checking if the
// output is a terminal is generally sufficient as all modern terminals support ANSI escape
// codes, so we'll stay with this simple solution for now.
let long_help = topic_command.render_long_help();
if out.is_terminal() {
writeln!(out, "{}", long_help.ansi())
} else {
writeln!(out, "{long_help}")View on GitHub (pinned to 2497b8007a)
Solutions
- Locate the HelpTopic enum and the clap Args definition; add the missing subcommand with a name equal to topic.name()
- If the topic was renamed, align both sides to the same name
- Add a test that enumerates every HelpTopic and asserts find_subcommand_mut succeeds for each
Example fix
// before: HelpTopic::Onboarding exists in the enum, but no matching subcommand is declared
// after: declare the subcommand so names line up with topic.name()
#[derive(clap::Subcommand)]
enum HelpSubcommands {
Stacks,
Onboarding, // must match HelpTopic::Onboarding.name() == "onboarding"
} Defensive patterns
Strategy: validation
Validate before calling
// CI test: every help topic must resolve to a clap subcommand
#[test]
fn all_help_topics_have_clap_metadata() {
use clap::CommandFactory;
let mut cmd = Args::command();
let help = cmd.find_subcommand_mut("help").expect("help subcommand");
for topic in HelpTopic::all() { // adjust to the actual enumeration
assert!(help.find_subcommand_mut(topic.name()).is_some(), "missing: {}", topic.name());
}
} Prevention
- Keep the HelpTopic enum and the clap subcommand list in the same file or an adjacent module
- Make the topic-to-subcommand name derived (single source of truth) instead of duplicated strings
- Run the enumeration test in CI so desync fails the build, not users
When it happens
Trigger: A maintainer adds a HelpTopic variant without declaring the matching 'help <name>' subcommand; a topic is renamed in the enum but not in the clap definition (or vice versa); conditional compilation drops the subcommand while the enum variant remains.
Common situations: Refactors touching help topics; running a locally built binary mid-refactor; generated topic lists drifting from hand-maintained clap registrations.
Related errors
- valid hex prefix
- object for prefix exists
- 'dot' (graphviz) must be installed on the system
- failed to create app settings
- target OID must exist when ahead calculation is enabled
AI-assisted analysis of gitbutlerapp/gitbutler@2497b8007a (2026-08-17).
Data as JSON: /api/errors/6d52d23f50170d9b.
Report an issue: GitHub.