[Bug] mqadmin: the declared option set does not match what the commands read (NPE instead of a usage error; the consumer -i mode is unreachable)

未关闭
#11,410 1 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

@iloveeyjafjalla 已经在做这个了。

开始于 2026年10月13日。

  • #11411 来自 @iloveeyjafjalla —— 未关闭

评估

难度
2/5
预计耗时
半天
新手友好度
25/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
java
领域
cli

调研方向

Start with the option declarations in tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerSubCommand.java, ResetMasterFlushOffsetSubCommand, and message/DecodeMessageIdCommond.java, comparing each Option set with the getOptionValue calls in execute(). Linked PR #11411 is already open against this issue, so check it before starting. Done means consumer -i parses, and the other two commands reject a missing -b/-o/-i at parse time.

由索引模型根据 Issue 内容生成。

描述

Before Creating the Bug Report
  • I found a bug, not just asking a question, which should be created in GitHub Discussions.
  • I have searched the GitHub Issues and GitHub Discussions of this repository and believe that this is not a duplicate.
  • I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.
Runtime platform environment

OS: any (the mqadmin command line tool)

RocketMQ version

branch: develop
Git commit id: a7edeecfb

JDK Version

Compiler: Temurin 1.8.0_504 (target 1.8, same as CI)

Describe the Bug

I cross-checked every commons-cli Option a subcommand declares against the keys the same command reads (hasOption/getOptionValue) across the 73 SubCommand classes of the tools module. Apart from the static-topic -mf/--mapFile mismatch that is already reported in #11381, three commands still declare an option set that does not match what their execute() needs:

(1) mqadmin consumer: -i is read but never declared, so the single-client mode cannot be used.

ConsumerSubCommand.buildCommandlineOptions only registers -g/--consumerGroup and -s/--jstack (tools/src/main/java/org/apache/rocketmq/tools/command/consumer/ConsumerSubCommand.java:53-64), while execute() branches on the option:

if (!commandLine.hasOption('i')) {
    ... // every client of the group
} else {
    String clientId = commandLine.getOptionValue('i').trim();   // :125
    ...
}

Because DefaultParser rejects an option that is not registered, mqadmin consumer -g <group> -i <clientId> aborts with Unrecognized option: -i and System.exit(1) (srvutil/.../ServerUtil.java:54-58); the else branch is therefore unreachable dead code and the per-client query the command was written to support cannot be run at all.

(2) mqadmin resetMasterFlushOffset: -b and -o are declared optional but read unconditionally.

Option opt = new Option("b", "brokerAddr", true, "which broker to reset");
opt.setRequired(false);                     // :41-42
opt = new Option("o", "offset", true, "the offset to reset at");
opt.setRequired(false);                     // :45-46
...
String brokerAddr = commandLine.getOptionValue('b').trim();            // :60
long masterFlushOffset = Long.parseLong(commandLine.getOptionValue('o').trim());   // :61

With either option omitted the parse succeeds and the command fails with a NullPointerException from the .trim() (there is no fallback for either value): mqadmin resetMasterFlushOffset crashes on -b, mqadmin resetMasterFlushOffset -b 127.0.0.1:10911 crashes on -o, and -o with a non-numeric value gives a NumberFormatException. The exception is wrapped into a SubCommandException and printed as a stack trace by MQAdminStartup (tools/.../command/MQAdminStartup.java:194-196), so the user gets no usage information at all.

(3) mqadmin decodeMessageId: -i is declared optional but read unconditionally.

DecodeMessageIdCommond.buildCommandlineOptions marks -i/--messageId setRequired(false) (tools/.../command/message/DecodeMessageIdCommond.java:41-42) and execute() immediately does commandLine.getOptionValue('i').trim() (:50, outside the try), so running the command without -i throws a NullPointerException instead of reporting the missing argument.

Steps to Reproduce
  1. mqadmin consumer -g myGroup -i 172.17.0.1@1234 → Unrecognized option: -i plus the help text, exit code 1, although execute() implements exactly this query.
  2. mqadmin resetMasterFlushOffset (or with only -b, or with only -o) → SubCommandException: ResetMasterFlushOffsetSubCommand command failed caused by java.lang.NullPointerException.
  3. mqadmin decodeMessageId → java.lang.NullPointerException from DecodeMessageIdCommond.execute.

At the unit level each case is reproduced by parsing the command's own option set with a plain DefaultParser (the pattern the module's existing tests use): the invocation either parses or it does not, and the missing-argument behavior is what the user sees before execute() runs.

What Did You Expect to See?
  • consumer -i <clientId> is accepted and performs the single-client query, like the sibling consumerStatus command whose -i/--clientId option is declared.
  • resetMasterFlushOffset without -b/-o and decodeMessageId without -i are rejected by the parser with the standard Missing required option: -x message and the command's help, the way every other mqadmin command whose arguments are mandatory behaves — instead of reaching execute() and dying with a NullPointerException stack trace.
What Did You See Instead?

Either an option the command actually reads is rejected as unknown, or a mandatory argument that is missing produces a raw NullPointerException / NumberFormatException with no usage hint. Both make the tool's own contract unreadable: mqadmin help <command> cannot even tell which arguments are mandatory, because they are declared optional.

Additional Context

Minimal fix: declare the missing option and mark the mandatory ones required —

  • ConsumerSubCommand: add new Option("i", "clientId", true, "The consumer's client id") (optional, as in ConsumerStatusSubCommand);
  • ResetMasterFlushOffsetSubCommand: -b/--brokerAddr and -o/--offset become required;
  • DecodeMessageIdCommond: -i/--messageId becomes required.

Each command keeps its documented usage working (the flags are only made mandatory where execute() has no alternative path). I will open a PR with the changes plus tests that parse each command's own Options (asserting the missing-argument rejection and that the normal invocation still parses).

主要语言
Java
星标
22.7k
派生
12k
平均合并
3 天 18 小时
30 天内合并 PR
17

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

apache/rocketmq 的其他 Issue

查看 apache/rocketmq 的全部 Issue

相似的 Issue

更多 Java Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。