Skip to content

Fix --debug rejected on many subcommands#1938

Open
mliem2k wants to merge 1 commit into
apple:mainfrom
mliem2k:fix-debug-flag-defaultsubcommand
Open

Fix --debug rejected on many subcommands#1938
mliem2k wants to merge 1 commit into
apple:mainfrom
mliem2k:fix-debug-flag-defaultsubcommand

Conversation

@mliem2k

@mliem2k mliem2k commented Jul 12, 2026

Copy link
Copy Markdown

Summary

Fixes #360.

container system status --debug (and many other subcommands) rejected --debug with:

Error: Unknown option '--debug'. Did you mean '--debug'?
Usage: container system status [--prefix <prefix>] [--format <format>] [--debug]

even though --debug is listed in the command's own usage text.

Root cause: Application's CommandConfiguration set defaultSubcommand: DefaultCommand.self (used to dispatch unrecognized top-level input to plugins). DefaultCommand declares @Argument(parsing: .captureForPassthrough) var remaining: [String]. That specific combination — defaultSubcommand pointing at a command with a .captureForPassthrough/.allRemainingInput argument — breaks ArgumentParser's flag recognition for unrelated, legitimately-matched subcommands throughout the whole tree, not just the default/plugin path.

I confirmed this against a minimal, isolated swift-argument-parser reproduction (root command with defaultSubcommand + .captureForPassthrough, and nested nested subcommands several levels down) — the bug reproduces on ArgumentParser 1.7.0 through the latest 1.8.2, and disappears immediately when defaultSubcommand is removed. It is not about the leaf command's own option declarations — commands with logOptions declared in different orders/positions, with or without extra flags, are affected identically as long as defaultSubcommand is present at the root.

In apple/container this affected (at least): system status/version/df, builder status/delete, image inspect/delete/pull/tag/save, network inspect, volume inspect — all rejected --debug when it appeared after the subcommand name.

Fix

  • Remove defaultSubcommand from Application's CommandConfiguration.
  • Add Application.pluginDispatchArguments(_:), which inspects the raw args before calling parseAsRoot and decides whether they should go to DefaultCommand (plugin dispatch, or the plugin-aware bare/--debug-only help text) or be parsed normally as one of Application's own subcommands. This preserves DefaultCommand's existing plugin-dispatch behavior — including passing a plugin's own --debug flag through to it unchanged, rather than swallowing it (which is what I found happens if you instead switch DefaultCommand to ArgumentParser's .allUnrecognized strategy — a plugin invoking container my-plugin --debug would silently lose its own --debug to the root's global flag).
  • main() also replicates the Application.validate() side effects (debug log level, Rosetta check) that would otherwise have run on the way to the default subcommand, so plugin dispatch keeps the same behavior for container --debug some-plugin ....

Before / after

$ container s status --debug
Error: Unknown option '--debug'. Did you mean '--debug'?
Usage: container system status [--prefix <prefix>] [--format <format>] [--debug]
  See 'container system status --help' for more information.

After the fix (daemon not running in this sandbox, but the flag is now recognized and parsing succeeds):

$ container s status --debug
Warning! Running debug build. Performance may be degraded.
apiserver is not running and not registered with launchd

Verified unchanged behavior for: bare container (still shows the plugin-aware help with the PLUGINS: section), container --help/-h, container --version, container --debug, unknown-plugin dispatch (container some-plugin --flag arg, including a plugin's own --debug passed through untouched), and normal subcommands like container image list --debug / container list --debug.

Testing

  • swift build succeeds.
  • Added Tests/ContainerCommandsTests/PluginDispatchArgumentsTests.swift (unit tests for the new dispatch-routing logic).
  • Added Tests/IntegrationTests/System/TestCLIDebugFlag.swift (regression coverage running the actual CLI for the commands reported in [Bug]: debug flag not accepted #1936).
  • Ran swift test for the affected suites: ContainerCommandsTests, TestCLIHelp, TestCLIStatus, TestCLIPluginErrors, TestCLIDebugFlag, PluginDispatchArgumentsTests — all 47 tests pass.

Test plan

  • swift build succeeds
  • container system status --debug no longer errors on --debug
  • container --help, -h, --version, bare container unchanged
  • Plugin dispatch unchanged, including a plugin's own --debug passed through
  • New and existing relevant tests pass (swift test --filter ...)

Application routed unrecognized top-level input to DefaultCommand via
ArgumentParser's defaultSubcommand, which combined with DefaultCommand's
.captureForPassthrough argument. That combination breaks flag parsing
for unrelated, legitimately-matched subcommands throughout the tree:
`container system status --debug`, `container system version --debug`,
`container image inspect/delete/pull/tag/save --debug`, `container
volume/network inspect --debug`, and `container builder status/delete
--debug` all rejected --debug with "Unknown option '--debug'. Did you
mean '--debug'?", even though it's listed in each command's own usage
text. Confirmed with a minimal ArgumentParser reproduction that this
persists through the latest 1.8.2 release and is triggered purely by
defaultSubcommand pointing to a command with a .captureForPassthrough
argument, regardless of the leaf command's own option declarations.

Remove defaultSubcommand from Application's configuration and instead
decide up front, in main(), whether input should be routed to
DefaultCommand (plugin dispatch, or the plugin-aware help text) versus
parsed normally via parseAsRoot. This keeps DefaultCommand's plugin
dispatch behavior (including passing a plugin's own --debug flag
through unchanged, rather than swallowing it) while no longer relying
on the ArgumentParser mechanism that broke parsing elsewhere.
@jglogan

jglogan commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

@mliem2k We're still working on some issues that are preventing us from spending more time on contributor PRs, but in the meantime I closed your issue as a dupe of #360 and updated your PR description to match.

There is another PR open for this but at first glance, what you've done seems like it could be a better approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: "Error: Unknown option '--debug'. Did you mean '--debug'?"

2 participants