[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)
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- Half a day
- Newbie friendliness
- 25/100
Research direction
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.
Written by the indexing model from the issue text.
Description
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
mqadmin consumer -g myGroup -i 172.17.0.1@1234→Unrecognized option: -iplus the help text, exit code 1, althoughexecute()implements exactly this query.mqadmin resetMasterFlushOffset(or with only-b, or with only-o) →SubCommandException: ResetMasterFlushOffsetSubCommand command failedcaused byjava.lang.NullPointerException.mqadmin decodeMessageId→java.lang.NullPointerExceptionfromDecodeMessageIdCommond.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 siblingconsumerStatuscommand whose-i/--clientIdoption is declared.resetMasterFlushOffsetwithout-b/-oanddecodeMessageIdwithout-iare rejected by the parser with the standardMissing required option: -xmessage and the command's help, the way every othermqadmincommand whose arguments are mandatory behaves — instead of reachingexecute()and dying with aNullPointerExceptionstack 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: addnew Option("i", "clientId", true, "The consumer's client id")(optional, as inConsumerStatusSubCommand);ResetMasterFlushOffsetSubCommand:-b/--brokerAddrand-o/--offsetbecome required;DecodeMessageIdCommond:-i/--messageIdbecomes 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).
- Dominant language
- Java
- Stars
- 22.7k
- Forks
- 12k
- Avg merge
- 3d 18h
- Merged PRs (30d)
- 17
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from apache/rocketmq
-
[Bug] RemotingHelper.connect swaps the socket send and receive buffer configurationPossibly taken @amalsaidov claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
CompressionType.of fails under the Turkish localePossibly taken @sharanggupta claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
[rocketmq-client-python] TransactionMQProducer allocates an unused producer during initializationOpen
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day
-
[Bug] notifyMasterOnline aborts the long-polling wake-up with a NullPointerException when an arrival drains a bucket firstPossibly taken @iloveeyjafjalla claimed this 29 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
apache/rocketmq#11441 · 1 comment ·
Maintainers usually reply within 1 day
-
[Enhancement] FilterFactory duplicate-registration and unregister semantics lack testsPossibly taken @chennaji9 claimed this 34 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
apache/rocketmq#11340 · 2 comments ·
Maintainers usually reply within 1 day
Similar issues
-
bug emitter:client:csharp
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
microsoft/typespec#12572 · 1 comment · 1 reaction ·
Maintainers usually reply within 1 day
-
GCC 15.2: -Wcast-qual warning - cast discards const qualifier from pointer target type (H5trace.c)Open
Difficulty 2/5 1-3 hours Newbie friendliness 73/100
Maintainers usually reply within 1 day
-
needs-area-label
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days
-
area/ai area/backend component/zeebe kind/task
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
spAnser/reacharound#38 ·