diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index e1e1874ec..bdf6479cf 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,5 +1,37 @@
# picocli Release Notes
+# Picocli 3.0.2
+The picocli community is pleased to announce picocli 3.0.2.
+
+This release fixes a bug for programmatic configuration.
+
+This is the thirty-first public release.
+Picocli follows [semantic versioning](http://semver.org/).
+
+## Table of Contents
+* [New and noteworthy](#3.0.2-new)
+* [Promoted features](#3.0.2-promoted)
+* [Fixed issues](#3.0.2-fixes)
+* [Deprecations](#3.0.2-deprecated)
+* [Potential breaking changes](#3.0.2-breaking-changes)
+
+## New and Noteworthy
+
+## Promoted Features
+Promoted features are features that were incubating in previous versions of picocli but are now supported and subject to backwards compatibility.
+
+No features have been promoted in this picocli release.
+
+## Fixed issues
+- [#381] Bugfix: Prevent NPE when adding programmatically created subcommands to CommandLine. Thanks to [Mikusch](https://github.com/Mikusch) for the bug report.
+
+## Deprecations
+No features were deprecated in this release.
+
+## Potential breaking changes
+This release has no breaking changes.
+
+
# Picocli 3.0.1
The picocli community is pleased to announce picocli 3.0.1.
diff --git a/src/main/java/picocli/CommandLine.java b/src/main/java/picocli/CommandLine.java
index fbb504a2f..72582f63a 100644
--- a/src/main/java/picocli/CommandLine.java
+++ b/src/main/java/picocli/CommandLine.java
@@ -4171,6 +4171,7 @@ private static void initSubcommands(Command cmd, CommandSpec parent, IFactory fa
}
}
static void initParentCommand(Object subcommand, Object parent) {
+ if (subcommand == null) { return; }
try {
Class> cls = subcommand.getClass();
while (cls != null) {
diff --git a/src/test/java/picocli/CommandLineModelTest.java b/src/test/java/picocli/CommandLineModelTest.java
index d41312839..e60401990 100644
--- a/src/test/java/picocli/CommandLineModelTest.java
+++ b/src/test/java/picocli/CommandLineModelTest.java
@@ -1572,4 +1572,24 @@ public T set(T value) throws Exception {
assertEquals(null, values.get(0));
assertEquals("2", values.get(1));
}
+
+ @Test
+ public void test381_NPE_whenAddingSubcommand() {
+ CommandSpec toplevel = CommandSpec.create();
+ toplevel.addOption(OptionSpec.builder("-o").description("o option").build());
+
+ CommandSpec sub = CommandSpec.create();
+ sub.addOption(OptionSpec.builder("-x").description("x option").build());
+
+ CommandLine commandLine = new CommandLine(toplevel);
+ commandLine.addSubcommand("sub", sub); // NPE here
+ commandLine.usage(System.out);
+
+ String expected = String.format("" +
+ "Usage: [-o] [COMMAND]%n" +
+ " -o o option%n" +
+ "Commands:%n" +
+ " sub%n");
+ assertEquals(expected, systemOutRule.getLog());
+ }
}